summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk4
-rw-r--r--api/current.txt787
-rw-r--r--core/java/android/app/Activity.java151
-rw-r--r--core/java/android/app/ActivityManager.java14
-rw-r--r--core/java/android/app/ActivityManagerNative.java21
-rw-r--r--core/java/android/app/ActivityThread.java75
-rw-r--r--core/java/android/app/AppOpsManager.java11
-rw-r--r--core/java/android/app/ApplicationThreadNative.java13
-rw-r--r--core/java/android/app/ContextImpl.java5
-rw-r--r--core/java/android/app/IActivityManager.java5
-rw-r--r--core/java/android/app/IApplicationThread.java5
-rw-r--r--core/java/android/app/IUiAutomationConnection.aidl1
-rw-r--r--core/java/android/app/Instrumentation.java95
-rw-r--r--core/java/android/app/KeyguardManager.java4
-rw-r--r--core/java/android/app/Notification.java4
-rw-r--r--core/java/android/app/UiAutomation.java41
-rw-r--r--core/java/android/app/UiAutomationConnection.java46
-rw-r--r--core/java/android/app/UsageStats.aidl20
-rw-r--r--core/java/android/app/UsageStats.java406
-rw-r--r--core/java/android/app/UsageStatsManager.java51
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java103
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl4
-rw-r--r--core/java/android/content/Context.java10
-rw-r--r--core/java/android/content/pm/PackageManager.java10
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java17
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java17
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java10
-rw-r--r--core/java/android/inputmethodservice/IInputMethodSessionWrapper.java12
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java23
-rw-r--r--core/java/android/net/BaseNetworkStateTracker.java11
-rw-r--r--core/java/android/net/ConnectivityManager.java5
-rw-r--r--core/java/android/net/Network.aidl (renamed from core/java/com/android/internal/os/PkgUsageStats.aidl)24
-rw-r--r--core/java/android/net/Network.java59
-rw-r--r--core/java/android/net/NetworkStateTracker.java10
-rw-r--r--core/java/android/net/Proxy.java4
-rw-r--r--core/java/android/nfc/INfcCardEmulation.aidl2
-rw-r--r--core/java/android/nfc/cardemulation/AidGroup.java24
-rw-r--r--core/java/android/nfc/cardemulation/ApduServiceInfo.java14
-rw-r--r--core/java/android/nfc/cardemulation/CardEmulation.java124
-rw-r--r--core/java/android/os/BatteryStats.java221
-rw-r--r--core/java/android/os/INetworkManagementService.aidl10
-rw-r--r--core/java/android/os/IUserManager.aidl1
-rw-r--r--core/java/android/os/ParcelableParcel.aidl19
-rw-r--r--core/java/android/os/UserManager.java23
-rw-r--r--core/java/android/print/PrintManager.java62
-rw-r--r--core/java/android/provider/Settings.java6
-rw-r--r--core/java/android/service/notification/INotificationListener.aidl10
-rw-r--r--core/java/android/service/notification/NotificationListenerService.java82
-rw-r--r--core/java/android/service/notification/NotificationOrderUpdate.aidl19
-rw-r--r--core/java/android/service/notification/NotificationOrderUpdate.java62
-rw-r--r--core/java/android/view/KeyEvent.java4
-rw-r--r--core/java/android/view/RenderNodeAnimator.java35
-rw-r--r--core/java/android/view/inputmethod/CorrectionInfo.java19
-rw-r--r--core/java/android/view/inputmethod/CursorAnchorInfo.aidl19
-rw-r--r--core/java/android/view/inputmethod/CursorAnchorInfo.java449
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java28
-rw-r--r--core/java/android/view/inputmethod/InputMethodSession.java12
-rw-r--r--core/java/android/view/inputmethod/SparseRectFArray.java265
-rw-r--r--core/java/android/webkit/EventLogTags.logtags1
-rw-r--r--core/java/android/webkit/PermissionRequest.java1
-rw-r--r--core/java/android/webkit/WebViewFactory.java22
-rw-r--r--core/java/android/widget/AbsListView.java94
-rw-r--r--core/java/android/widget/AbsSeekBar.java219
-rw-r--r--core/java/android/widget/EdgeEffect.java221
-rw-r--r--core/java/android/widget/HorizontalScrollView.java6
-rw-r--r--core/java/android/widget/ProgressBar.java21
-rw-r--r--core/java/android/widget/ScrollView.java6
-rw-r--r--core/java/android/widget/Switch.java90
-rw-r--r--core/java/com/android/internal/app/AlertController.java74
-rw-r--r--core/java/com/android/internal/app/IUsageStats.aidl10
-rw-r--r--core/java/com/android/internal/app/ProcessStats.java7
-rw-r--r--core/java/com/android/internal/notification/DemoContactNotificationScorer.java188
-rw-r--r--core/java/com/android/internal/notification/NotificationScorer.java27
-rw-r--r--core/java/com/android/internal/notification/PeopleNotificationScorer.java227
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java82
-rw-r--r--core/java/com/android/internal/os/PkgUsageStats.java94
-rw-r--r--core/java/com/android/internal/util/AsyncChannel.java3
-rw-r--r--core/java/com/android/internal/util/VirtualRefBasePtr.java47
-rw-r--r--core/java/com/android/internal/view/IInputMethodSession.aidl3
-rw-r--r--core/jni/Android.mk3
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android/graphics/Bitmap.cpp1674
-rw-r--r--core/jni/android/graphics/CanvasProperty.cpp16
-rw-r--r--core/jni/android_media_AudioTrack.cpp48
-rw-r--r--core/jni/android_view_RenderNodeAnimator.cpp9
-rw-r--r--core/jni/com_android_internal_util_VirtualRefBasePtr.cpp49
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.pngbin0 -> 211 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.pngbin0 -> 227 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.pngbin0 -> 219 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.pngbin0 -> 206 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.pngbin0 -> 169 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.pngbin0 -> 174 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.pngbin0 -> 227 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.pngbin0 -> 225 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.pngbin0 -> 300 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.pngbin0 -> 353 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.pngbin0 -> 355 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.pngbin0 -> 364 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.pngbin0 -> 373 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.pngbin0 -> 365 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.pngbin0 -> 355 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.pngbin0 -> 338 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.pngbin0 -> 541 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.pngbin0 -> 523 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.pngbin0 -> 428 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.pngbin0 -> 383 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.pngbin0 -> 533 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.pngbin0 -> 640 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.pngbin0 -> 649 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.pngbin0 -> 694 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.pngbin0 -> 706 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.pngbin0 -> 648 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.pngbin0 -> 684 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.pngbin0 -> 678 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.pngbin0 -> 677 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.pngbin0 -> 649 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.pngbin0 -> 657 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.pngbin0 -> 666 bytes
-rw-r--r--core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.pngbin341 -> 232 bytes
-rw-r--r--core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.pngbin241 -> 184 bytes
-rw-r--r--core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.pngbin546 -> 523 bytes
-rw-r--r--core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.pngbin222 -> 187 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.pngbin0 -> 127 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.pngbin0 -> 138 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.pngbin0 -> 155 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.pngbin0 -> 134 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.pngbin0 -> 134 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.pngbin0 -> 123 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.pngbin0 -> 143 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.pngbin0 -> 159 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.pngbin0 -> 184 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.pngbin0 -> 184 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.pngbin0 -> 191 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.pngbin0 -> 197 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.pngbin0 -> 197 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.pngbin0 -> 194 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.pngbin0 -> 192 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.pngbin0 -> 189 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.pngbin0 -> 253 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.pngbin0 -> 240 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.pngbin0 -> 202 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.pngbin0 -> 207 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.pngbin0 -> 263 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.pngbin0 -> 260 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.pngbin0 -> 278 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.pngbin0 -> 287 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.pngbin0 -> 295 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.pngbin0 -> 285 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.pngbin0 -> 292 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.pngbin0 -> 293 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.pngbin0 -> 281 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.pngbin0 -> 283 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.pngbin0 -> 285 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.pngbin0 -> 283 bytes
-rw-r--r--core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.pngbin230 -> 181 bytes
-rw-r--r--core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.pngbin182 -> 154 bytes
-rw-r--r--core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.pngbin390 -> 371 bytes
-rw-r--r--core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.pngbin184 -> 156 bytes
-rw-r--r--core/res/res/drawable-nodpi/stat_sys_adb.xml2
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.pngbin0 -> 186 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.pngbin0 -> 206 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.pngbin0 -> 220 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.pngbin0 -> 201 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.pngbin0 -> 178 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.pngbin0 -> 177 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.pngbin0 -> 228 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.pngbin0 -> 233 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.pngbin0 -> 272 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.pngbin0 -> 308 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.pngbin0 -> 305 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.pngbin0 -> 326 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.pngbin0 -> 325 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.pngbin0 -> 313 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.pngbin0 -> 310 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.pngbin0 -> 300 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.pngbin0 -> 474 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.pngbin0 -> 433 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.pngbin0 -> 359 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.pngbin0 -> 353 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.pngbin0 -> 488 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.pngbin0 -> 507 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.pngbin0 -> 547 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.pngbin0 -> 523 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.pngbin0 -> 560 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.pngbin0 -> 550 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.pngbin0 -> 556 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.pngbin0 -> 558 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.pngbin0 -> 566 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.pngbin0 -> 571 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.pngbin0 -> 561 bytes
-rw-r--r--core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.pngbin0 -> 555 bytes
-rw-r--r--core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.pngbin495 -> 314 bytes
-rw-r--r--core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.pngbin304 -> 248 bytes
-rw-r--r--core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.pngbin827 -> 767 bytes
-rw-r--r--core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.pngbin279 -> 223 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.pngbin0 -> 513 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.pngbin0 -> 565 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.pngbin0 -> 605 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.pngbin0 -> 536 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.pngbin0 -> 436 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.pngbin0 -> 403 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.pngbin0 -> 593 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.pngbin0 -> 613 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.pngbin0 -> 757 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.pngbin0 -> 826 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.pngbin0 -> 832 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.pngbin0 -> 890 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.pngbin0 -> 886 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.pngbin0 -> 886 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.pngbin0 -> 838 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.pngbin0 -> 808 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00000_qntm_alpha.pngbin0 -> 1821 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00001_qntm_alpha.pngbin0 -> 1636 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00002_qntm_alpha.pngbin0 -> 1227 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00003_qntm_alpha.pngbin0 -> 1139 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00004_qntm_alpha.pngbin0 -> 1700 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00005_qntm_alpha.pngbin0 -> 2050 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00006_qntm_alpha.pngbin0 -> 2205 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00007_qntm_alpha.pngbin0 -> 2185 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00008_qntm_alpha.pngbin0 -> 2193 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00009_qntm_alpha.pngbin0 -> 2285 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00010_qntm_alpha.pngbin0 -> 2233 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00011_qntm_alpha.pngbin0 -> 2255 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00012_qntm_alpha.pngbin0 -> 2280 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00013_qntm_alpha.pngbin0 -> 2280 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00014_qntm_alpha.pngbin0 -> 2274 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/btn_radio_anim_00015_qntm_alpha.pngbin0 -> 2181 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.pngbin1014 -> 388 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.pngbin881 -> 294 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.pngbin1153 -> 1089 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.pngbin508 -> 293 bytes
-rw-r--r--core/res/res/drawable/btn_check_quantum_anim.xml106
-rw-r--r--core/res/res/drawable/btn_radio_quantum_anim.xml104
-rw-r--r--core/res/res/drawable/scrubber_progress_horizontal_quantum.xml16
-rw-r--r--core/res/res/layout/alert_dialog_micro.xml2
-rw-r--r--core/res/res/values/attrs.xml60
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/dimens.xml2
-rw-r--r--core/res/res/values/public.xml4
-rw-r--r--core/res/res/values/styles_micro.xml10
-rw-r--r--core/res/res/values/styles_quantum.xml6
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--core/res/res/values/themes_micro.xml7
-rw-r--r--core/res/res/values/themes_quantum.xml8
-rwxr-xr-xcore/tests/inputmethodtests/run_core_inputmethod_test.sh2
-rw-r--r--core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java164
-rw-r--r--core/tests/inputmethodtests/src/android/os/SparseRectFArrayTest.java233
-rw-r--r--docs/html/guide/topics/manifest/uses-feature-element.jd11
-rw-r--r--docs/html/guide/topics/resources/string-resource.jd107
-rw-r--r--docs/html/guide/topics/ui/notifiers/notifications.jd21
-rw-r--r--docs/html/wear/images/notif_summary_framed.pngbin0 -> 39277 bytes
-rw-r--r--docs/html/wear/notifications/stacks.jd90
-rw-r--r--graphics/java/android/graphics/CanvasProperty.java20
-rw-r--r--graphics/java/android/graphics/SurfaceTexture.java16
-rw-r--r--graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java524
-rw-r--r--graphics/java/android/graphics/drawable/AnimationDrawable.java2
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java2
-rw-r--r--graphics/java/android/graphics/drawable/DrawableContainer.java10
-rw-r--r--graphics/java/android/graphics/drawable/StateListDrawable.java31
-rw-r--r--libs/androidfw/ResourceTypes.cpp91
-rw-r--r--libs/hwui/Android.mk3
-rw-r--r--libs/hwui/Animator.h2
-rw-r--r--libs/hwui/CanvasProperty.h3
-rw-r--r--libs/hwui/DisplayList.h1
-rw-r--r--libs/hwui/DisplayListRenderer.cpp2
-rw-r--r--libs/hwui/Matrix.cpp4
-rw-r--r--libs/hwui/Matrix.h2
-rw-r--r--libs/hwui/OpenGLRenderer.cpp43
-rw-r--r--libs/hwui/OpenGLRenderer.h14
-rw-r--r--libs/hwui/Rect.h8
-rw-r--r--libs/hwui/RenderNode.h1
-rw-r--r--libs/hwui/Snapshot.cpp26
-rw-r--r--libs/hwui/font/Font.cpp20
-rw-r--r--libs/hwui/utils/GLUtils.cpp52
-rw-r--r--libs/hwui/utils/GLUtils.h (renamed from libs/hwui/utils/VirtualLightRefBase.h)21
-rw-r--r--libs/hwui/utils/MathUtils.h2
-rw-r--r--media/java/android/media/AudioAttributes.java444
-rw-r--r--media/java/android/media/AudioFormat.java2
-rw-r--r--media/java/android/media/AudioService.java81
-rw-r--r--media/java/android/media/AudioTrack.java113
-rw-r--r--media/java/android/media/MediaCodec.java56
-rw-r--r--media/jni/android_media_MediaCodec.cpp13
-rw-r--r--media/jni/android_media_MediaCodec.h3
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java2
-rw-r--r--packages/Keyguard/res/layout/keyguard_status_view.xml2
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.pngbin761 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_notify_open_normal.pngbin685 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.pngbin623 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.pngbin1821 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-ldrtl-hdpi/ic_notify_clear_normal.pngbin751 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-ldrtl-mdpi/ic_notify_clear_normal.pngbin660 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_notify_clear_normal.pngbin845 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_notify_clear_normal.pngbin3072 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.pngbin695 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_notify_open_normal.pngbin537 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.pngbin506 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.pngbin1251 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.pngbin874 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_notify_open_normal.pngbin939 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.pngbin812 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.pngbin2568 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_notify_clear_normal.pngbin1267 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_notify_open_normal.pngbin3651 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.pngbin3614 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.pngbin3247 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable/ic_settings_24dp.xml29
-rw-r--r--packages/SystemUI/res/drawable/notification_header_bg.xml (renamed from packages/SystemUI/res/layout/status_bar_flip_button.xml)22
-rw-r--r--packages/SystemUI/res/drawable/recents_dismiss_dark.xml35
-rw-r--r--packages/SystemUI/res/drawable/recents_dismiss_light.xml35
-rw-r--r--packages/SystemUI/res/layout/heads_up.xml2
-rw-r--r--packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml2
-rw-r--r--packages/SystemUI/res/layout/recents_task_view.xml7
-rw-r--r--packages/SystemUI/res/layout/status_bar.xml25
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml20
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded_header.xml78
-rw-r--r--packages/SystemUI/res/layout/status_bar_toggle_slider.xml1
-rw-r--r--packages/SystemUI/res/layout/super_status_bar.xml4
-rw-r--r--packages/SystemUI/res/layout/user_switcher_host.xml3
-rw-r--r--packages/SystemUI/res/values-sw600dp/styles.xml11
-rw-r--r--packages/SystemUI/res/values/attrs.xml7
-rw-r--r--packages/SystemUI/res/values/colors.xml13
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/res/values/dimens.xml20
-rw-r--r--packages/SystemUI/res/values/styles.xml19
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java64
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Constants.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java53
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Utilities.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java93
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java121
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java189
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java215
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java50
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java144
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java68
-rw-r--r--services/core/java/com/android/server/NativeDaemonConnector.java4
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java20
-rw-r--r--services/core/java/com/android/server/NetworkScoreService.java1
-rw-r--r--services/core/java/com/android/server/VibratorService.java35
-rw-r--r--services/core/java/com/android/server/WiredAccessoryManager.java28
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java22
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java2
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityStack.java14
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java8
-rw-r--r--services/core/java/com/android/server/am/UsageStatsService.java384
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java5
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java5
-rw-r--r--services/core/java/com/android/server/notification/NotificationComparator.java44
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java267
-rw-r--r--services/core/java/com/android/server/notification/NotificationSignalExtractor.java41
-rw-r--r--services/core/java/com/android/server/notification/NotificationUsageStats.java4
-rw-r--r--services/core/java/com/android/server/notification/RankingFuture.java118
-rw-r--r--services/core/java/com/android/server/notification/ValidateNotificationPeople.java298
-rwxr-xr-xservices/core/java/com/android/server/pm/PackageManagerService.java9
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java45
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java2
-rw-r--r--services/core/java/com/android/server/wm/AppTransition.java30
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java13
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java498
-rw-r--r--services/java/com/android/server/SystemServer.java10
-rw-r--r--tests/Split/Android.mk28
-rw-r--r--tests/Split/AndroidManifest.xml27
-rw-r--r--tests/Split/assets/blah.txt1
-rw-r--r--tests/Split/assets/statement.xml (renamed from packages/SystemUI/res/drawable/ic_notify_clear.xml)10
-rw-r--r--tests/Split/res/layout-fr-sw600dp/main.xml23
-rw-r--r--tests/Split/res/layout/main.xml (renamed from packages/SystemUI/res/drawable/ic_notifications.xml)12
-rw-r--r--tests/Split/res/values-de/values.xml (renamed from packages/SystemUI/res/drawable/ic_notify_settings.xml)12
-rw-r--r--tests/Split/res/values-fr/values.xml25
-rw-r--r--tests/Split/res/values-sw600dp/values.xml (renamed from packages/SystemUI/res/drawable/ic_notify_quicksettings.xml)12
-rw-r--r--tests/Split/res/values/values.xml47
-rw-r--r--tests/Split/src/java/com/android/example/split/ActivityMain.java31
-rw-r--r--tools/aapt/AaptAssets.cpp1515
-rw-r--r--tools/aapt/AaptAssets.h101
-rw-r--r--tools/aapt/AaptConfig.cpp790
-rw-r--r--tools/aapt/AaptConfig.h85
-rw-r--r--tools/aapt/AaptUtil.cpp64
-rw-r--r--tools/aapt/AaptUtil.h30
-rw-r--r--tools/aapt/Android.mk190
-rw-r--r--tools/aapt/ApkBuilder.cpp111
-rw-r--r--tools/aapt/ApkBuilder.h113
-rw-r--r--tools/aapt/Bundle.h11
-rw-r--r--tools/aapt/Command.cpp93
-rw-r--r--tools/aapt/ConfigDescription.h57
-rw-r--r--tools/aapt/Main.cpp26
-rw-r--r--tools/aapt/Main.h16
-rw-r--r--tools/aapt/OutputSet.h56
-rw-r--r--tools/aapt/Package.cpp91
-rw-r--r--tools/aapt/Resource.cpp141
-rw-r--r--tools/aapt/ResourceFilter.cpp145
-rw-r--r--tools/aapt/ResourceFilter.h132
-rw-r--r--tools/aapt/ResourceTable.cpp28
-rw-r--r--tools/aapt/ResourceTable.h37
-rw-r--r--tools/aapt/tests/AaptConfig_test.cpp78
-rw-r--r--tools/aapt/tests/AaptGroupEntry_test.cpp54
-rw-r--r--tools/aapt/tests/ResourceFilter_test.cpp128
-rw-r--r--tools/aapt/tests/TestHelper.h33
-rw-r--r--wifi/java/android/net/wifi/WifiConfiguration.java6
423 files changed, 12347 insertions, 5176 deletions
diff --git a/Android.mk b/Android.mk
index 99d73fa..5e634c1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -76,8 +76,8 @@ LOCAL_SRC_FILES += \
core/java/android/app/ISearchManagerCallback.aidl \
core/java/android/app/IServiceConnection.aidl \
core/java/android/app/IStopUserCallback.aidl \
- core/java/android/app/task/ITaskCallback.aidl \
- core/java/android/app/task/ITaskService.aidl \
+ core/java/android/app/task/ITaskCallback.aidl \
+ core/java/android/app/task/ITaskService.aidl \
core/java/android/app/IThumbnailRetriever.aidl \
core/java/android/app/ITransientNotification.aidl \
core/java/android/app/IUiAutomationConnection.aidl \
diff --git a/api/current.txt b/api/current.txt
index edc8700..5a22eb1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -567,6 +567,7 @@ package android {
field public static final int freezesText = 16843116; // 0x101016c
field public static final int fromAlpha = 16843210; // 0x10101ca
field public static final int fromDegrees = 16843187; // 0x10101b3
+ field public static final int fromId = 16843856; // 0x1010450
field public static final int fromScene = 16843741; // 0x10103dd
field public static final int fromXDelta = 16843206; // 0x10101c6
field public static final int fromXScale = 16843202; // 0x10101c2
@@ -956,6 +957,7 @@ package android {
field public static final int restoreAnyVersion = 16843450; // 0x10102ba
field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
field public static final int restrictedAccountType = 16843733; // 0x10103d5
+ field public static final int reversible = 16843857; // 0x1010451
field public static final int right = 16843183; // 0x10101af
field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
field public static final int ringtoneType = 16843257; // 0x10101f9
@@ -1045,6 +1047,7 @@ package android {
field public static final int spinnerStyle = 16842881; // 0x1010081
field public static final int spinnersShown = 16843595; // 0x101034b
field public static final int splitMotionEvents = 16843503; // 0x10102ef
+ field public static final int splitTrack = 16843858; // 0x1010452
field public static final int src = 16843033; // 0x1010119
field public static final int ssp = 16843747; // 0x10103e3
field public static final int sspPattern = 16843749; // 0x10103e5
@@ -1214,6 +1217,7 @@ package android {
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 = 16843855; // 0x101044f
field public static final int toScene = 16843742; // 0x10103de
field public static final int toXDelta = 16843207; // 0x10101c7
field public static final int toXScale = 16843203; // 0x10101c3
@@ -3258,6 +3262,7 @@ package android.app {
method public boolean onContextItemSelected(android.view.MenuItem);
method public void onContextMenuClosed(android.view.Menu);
method protected void onCreate(android.os.Bundle);
+ method protected void onCreate(android.os.Bundle, android.os.PersistableBundle);
method public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo);
method public java.lang.CharSequence onCreateDescription();
method protected deprecated android.app.Dialog onCreateDialog(int);
@@ -3288,6 +3293,7 @@ package android.app {
method public void onPanelClosed(int, android.view.Menu);
method protected void onPause();
method protected void onPostCreate(android.os.Bundle);
+ method protected void onPostCreate(android.os.Bundle, android.os.PersistableBundle);
method protected void onPostResume();
method protected deprecated void onPrepareDialog(int, android.app.Dialog);
method protected deprecated void onPrepareDialog(int, android.app.Dialog, android.os.Bundle);
@@ -3297,9 +3303,11 @@ package android.app {
method public void onProvideAssistData(android.os.Bundle);
method protected void onRestart();
method protected void onRestoreInstanceState(android.os.Bundle);
+ method protected void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
method protected void onResume();
method public deprecated java.lang.Object onRetainNonConfigurationInstance();
method protected void onSaveInstanceState(android.os.Bundle);
+ method protected void onSaveInstanceState(android.os.Bundle, android.os.PersistableBundle);
method public boolean onSearchRequested();
method protected void onStart();
method protected void onStop();
@@ -4195,14 +4203,18 @@ package android.app {
method public android.app.Instrumentation.ActivityMonitor addMonitor(android.content.IntentFilter, android.app.Instrumentation.ActivityResult, boolean);
method public android.app.Instrumentation.ActivityMonitor addMonitor(java.lang.String, android.app.Instrumentation.ActivityResult, boolean);
method public void callActivityOnCreate(android.app.Activity, android.os.Bundle);
+ method public void callActivityOnCreate(android.app.Activity, android.os.Bundle, android.os.PersistableBundle);
method public void callActivityOnDestroy(android.app.Activity);
method public void callActivityOnNewIntent(android.app.Activity, android.content.Intent);
method public void callActivityOnPause(android.app.Activity);
method public void callActivityOnPostCreate(android.app.Activity, android.os.Bundle);
+ method public void callActivityOnPostCreate(android.app.Activity, android.os.Bundle, android.os.PersistableBundle);
method public void callActivityOnRestart(android.app.Activity);
method public void callActivityOnRestoreInstanceState(android.app.Activity, android.os.Bundle);
+ method public void callActivityOnRestoreInstanceState(android.app.Activity, android.os.Bundle, android.os.PersistableBundle);
method public void callActivityOnResume(android.app.Activity);
method public void callActivityOnSaveInstanceState(android.app.Activity, android.os.Bundle);
+ method public void callActivityOnSaveInstanceState(android.app.Activity, android.os.Bundle, android.os.PersistableBundle);
method public void callActivityOnStart(android.app.Activity);
method public void callActivityOnStop(android.app.Activity);
method public void callActivityOnUserLeaving(android.app.Activity);
@@ -4838,6 +4850,7 @@ package android.app {
method public void clearWindowAnimationFrameStats();
method public boolean clearWindowContentFrameStats(int);
method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
+ method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String);
method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
@@ -5050,6 +5063,7 @@ package android.app.admin {
method public boolean isActivePasswordSufficient();
method public boolean isAdminActive(android.content.ComponentName);
method public boolean isDeviceOwnerApp(java.lang.String);
+ method public boolean isLockTaskPermitted(android.content.ComponentName);
method public boolean isProfileOwnerApp(java.lang.String);
method public void lockNow();
method public void removeActiveAdmin(android.content.ComponentName);
@@ -5058,6 +5072,7 @@ package android.app.admin {
method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
method public void setCameraDisabled(android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
+ method public void setLockTaskComponents(android.content.ComponentName[]) throws java.lang.SecurityException;
method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
method public void setMaximumTimeToLock(android.content.ComponentName, long);
method public void setPasswordExpirationTimeout(android.content.ComponentName, long);
@@ -8047,6 +8062,7 @@ package android.content.pm {
field public static final java.lang.String FEATURE_CAMERA = "android.hardware.camera";
field public static final java.lang.String FEATURE_CAMERA_ANY = "android.hardware.camera.any";
field public static final java.lang.String FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus";
+ field public static final java.lang.String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external";
field public static final java.lang.String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
field public static final java.lang.String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
field public static final java.lang.String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
@@ -10819,6 +10835,12 @@ package android.graphics.drawable {
method public abstract void stop();
}
+ public class AnimatedStateListDrawable extends android.graphics.drawable.StateListDrawable {
+ ctor public AnimatedStateListDrawable();
+ method public void addState(int[], android.graphics.drawable.Drawable, int);
+ method public void addTransition(int, int, android.graphics.drawable.AnimationDrawable, boolean);
+ }
+
public class AnimationDrawable extends android.graphics.drawable.DrawableContainer implements android.graphics.drawable.Animatable java.lang.Runnable {
ctor public AnimationDrawable();
method public void addFrame(android.graphics.drawable.Drawable, int);
@@ -12867,6 +12889,7 @@ package android.inputmethodservice {
method public void onStartInputView(android.view.inputmethod.EditorInfo, boolean);
method public void onUnbindInput();
method public void onUpdateCursor(android.graphics.Rect);
+ method public void onUpdateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo);
method public void onUpdateExtractedText(int, android.view.inputmethod.ExtractedText);
method public void onUpdateExtractingViews(android.view.inputmethod.EditorInfo);
method public void onUpdateExtractingVisibility(android.view.inputmethod.EditorInfo);
@@ -12916,6 +12939,7 @@ package android.inputmethodservice {
method public void finishInput();
method public void toggleSoftInput(int, int);
method public void updateCursor(android.graphics.Rect);
+ method public void updateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo);
method public void updateExtractedText(int, android.view.inputmethod.ExtractedText);
method public void updateSelection(int, int, int, int, int, int);
method public void viewClicked(boolean);
@@ -13317,6 +13341,45 @@ package android.media {
method public void stop();
}
+ public final class AudioAttributes {
+ method public int getContentType();
+ method public int getFlags();
+ method public java.util.Set<java.lang.String> getTags();
+ method public int getUsage();
+ 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
+ field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
+ field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
+ field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
+ field public static final int USAGE_ALARM = 4; // 0x4
+ field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
+ field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
+ field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
+ field public static final int USAGE_GAME = 14; // 0xe
+ field public static final int USAGE_MEDIA = 1; // 0x1
+ field public static final int USAGE_NOTIFICATION = 5; // 0x5
+ field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
+ field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
+ field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
+ field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
+ field public static final int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6; // 0x6
+ field public static final int USAGE_UNKNOWN = 0; // 0x0
+ field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
+ field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
+ }
+
+ 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);
+ method public android.media.AudioAttributes.Builder setLegacyStreamType(int);
+ method public android.media.AudioAttributes.Builder setUsage(int);
+ }
+
public class AudioFormat {
ctor public AudioFormat();
field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
@@ -13361,6 +13424,7 @@ package android.media {
field public static final int ENCODING_INVALID = 0; // 0x0
field public static final int ENCODING_PCM_16BIT = 2; // 0x2
field public static final int ENCODING_PCM_8BIT = 3; // 0x3
+ field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
}
public class AudioManager {
@@ -13578,6 +13642,7 @@ package android.media {
method public void stop() throws java.lang.IllegalStateException;
method public int write(byte[], int, int);
method public int write(short[], int, int);
+ method public int write(float[], int, int, int);
method public int write(java.nio.ByteBuffer, int, int);
field public static final int ERROR = -1; // 0xffffffff
field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
@@ -13810,6 +13875,7 @@ package android.media {
method public final void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
method public final void release();
method public final void releaseOutputBuffer(int, boolean);
+ method public final void releaseOutputBuffer(int, long);
method public void setNotificationCallback(android.media.MediaCodec.NotificationCallback);
method public final void setParameters(android.os.Bundle);
method public final void setVideoScalingMode(int);
@@ -17133,6 +17199,7 @@ package android.nfc.cardemulation {
}
public final class CardEmulation {
+ method public boolean categoryAllowsForegroundPreference(java.lang.String);
method public android.nfc.cardemulation.AidGroup getAidGroupForService(android.content.ComponentName, java.lang.String);
method public static synchronized android.nfc.cardemulation.CardEmulation getInstance(android.nfc.NfcAdapter);
method public int getSelectionModeForCategory(java.lang.String);
@@ -17140,6 +17207,8 @@ package android.nfc.cardemulation {
method public boolean isDefaultServiceForCategory(android.content.ComponentName, java.lang.String);
method public boolean registerAidGroupForService(android.content.ComponentName, android.nfc.cardemulation.AidGroup);
method public boolean removeAidGroupForService(android.content.ComponentName, java.lang.String);
+ method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
+ method public boolean unsetPreferredService(android.app.Activity);
field public static final java.lang.String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT";
field public static final java.lang.String CATEGORY_OTHER = "other";
field public static final java.lang.String CATEGORY_PAYMENT = "payment";
@@ -25153,16 +25222,24 @@ package android.service.notification {
method public final deprecated void cancelNotification(java.lang.String, java.lang.String, int);
method public final void cancelNotification(java.lang.String);
method public final void cancelNotifications(java.lang.String[]);
- method public java.lang.String[] getActiveNotificationKeys();
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[]);
+ method public java.lang.String[] getOrderedNotificationKeys();
method public android.os.IBinder onBind(android.content.Intent);
method public void onListenerConnected(java.lang.String[]);
+ method public void onNotificationOrderUpdate();
method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification);
method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
}
+ public class NotificationOrderUpdate implements android.os.Parcelable {
+ ctor public NotificationOrderUpdate(android.os.Parcel);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
+ }
+
public class StatusBarNotification implements android.os.Parcelable {
ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
ctor public StatusBarNotification(android.os.Parcel);
@@ -25716,6 +25793,637 @@ package android.speech.tts {
}
+package android.system {
+
+ public final class ErrnoException extends java.lang.Exception {
+ ctor public ErrnoException(java.lang.String, int);
+ ctor public ErrnoException(java.lang.String, int, java.lang.Throwable);
+ field public final int errno;
+ }
+
+ public final class Os {
+ method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
+ method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
+ method public static void bind(java.io.FileDescriptor, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
+ method public static void chmod(java.lang.String, int) throws android.system.ErrnoException;
+ method public static void chown(java.lang.String, int, int) throws android.system.ErrnoException;
+ method public static void close(java.io.FileDescriptor) throws android.system.ErrnoException;
+ method public static void connect(java.io.FileDescriptor, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
+ method public static java.io.FileDescriptor dup(java.io.FileDescriptor) throws android.system.ErrnoException;
+ method public static java.io.FileDescriptor dup2(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+ method public static java.lang.String[] environ();
+ method public static void execv(java.lang.String, java.lang.String[]) throws android.system.ErrnoException;
+ method public static void execve(java.lang.String, java.lang.String[], java.lang.String[]) throws android.system.ErrnoException;
+ method public static void fchmod(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+ method public static void fchown(java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
+ method public static void fdatasync(java.io.FileDescriptor) throws android.system.ErrnoException;
+ method public static android.system.StructStat fstat(java.io.FileDescriptor) throws android.system.ErrnoException;
+ method public static android.system.StructStatVfs fstatvfs(java.io.FileDescriptor) throws android.system.ErrnoException;
+ method public static void fsync(java.io.FileDescriptor) throws android.system.ErrnoException;
+ method public static void ftruncate(java.io.FileDescriptor, long) throws android.system.ErrnoException;
+ method public static java.lang.String gai_strerror(int);
+ method public static int getegid();
+ method public static java.lang.String getenv(java.lang.String);
+ method public static int geteuid();
+ method public static int getgid();
+ method public static java.net.SocketAddress getpeername(java.io.FileDescriptor) throws android.system.ErrnoException;
+ method public static int getpid();
+ method public static int getppid();
+ method public static java.net.SocketAddress getsockname(java.io.FileDescriptor) throws android.system.ErrnoException;
+ method public static int gettid();
+ method public static int getuid();
+ method public static java.lang.String if_indextoname(int);
+ method public static java.net.InetAddress inet_pton(int, java.lang.String);
+ method public static boolean isatty(java.io.FileDescriptor);
+ method public static void kill(int, int) throws android.system.ErrnoException;
+ method public static void lchown(java.lang.String, int, int) throws android.system.ErrnoException;
+ method public static void link(java.lang.String, java.lang.String) throws android.system.ErrnoException;
+ method public static void listen(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+ method public static long lseek(java.io.FileDescriptor, long, int) throws android.system.ErrnoException;
+ method public static android.system.StructStat lstat(java.lang.String) throws android.system.ErrnoException;
+ method public static void mincore(long, long, byte[]) throws android.system.ErrnoException;
+ method public static void mkdir(java.lang.String, int) throws android.system.ErrnoException;
+ method public static void mkfifo(java.lang.String, int) throws android.system.ErrnoException;
+ method public static void mlock(long, long) throws android.system.ErrnoException;
+ method public static long mmap(long, long, int, int, java.io.FileDescriptor, long) throws android.system.ErrnoException;
+ method public static void msync(long, long, int) throws android.system.ErrnoException;
+ method public static void munlock(long, long) throws android.system.ErrnoException;
+ method public static void munmap(long, long) throws android.system.ErrnoException;
+ method public static java.io.FileDescriptor open(java.lang.String, int, int) throws android.system.ErrnoException;
+ method public static java.io.FileDescriptor[] pipe() throws android.system.ErrnoException;
+ method public static int poll(android.system.StructPollfd[], int) throws android.system.ErrnoException;
+ method public static void posix_fallocate(java.io.FileDescriptor, long, long) throws android.system.ErrnoException;
+ method public static int prctl(int, long, long, long, long) throws android.system.ErrnoException;
+ method public static int pread(java.io.FileDescriptor, java.nio.ByteBuffer, long) throws android.system.ErrnoException, java.io.InterruptedIOException;
+ method public static int pread(java.io.FileDescriptor, byte[], int, int, long) throws android.system.ErrnoException, java.io.InterruptedIOException;
+ method public static int pwrite(java.io.FileDescriptor, java.nio.ByteBuffer, long) throws android.system.ErrnoException, java.io.InterruptedIOException;
+ method public static int pwrite(java.io.FileDescriptor, byte[], int, int, long) throws android.system.ErrnoException, java.io.InterruptedIOException;
+ method public static int read(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
+ method public static int read(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
+ method public static java.lang.String readlink(java.lang.String) throws android.system.ErrnoException;
+ method public static int readv(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
+ method public static int recvfrom(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
+ method public static int recvfrom(java.io.FileDescriptor, byte[], int, int, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
+ method public static void remove(java.lang.String) throws android.system.ErrnoException;
+ method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
+ method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
+ method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
+ method public static void setegid(int) throws android.system.ErrnoException;
+ method public static void setenv(java.lang.String, java.lang.String, boolean) throws android.system.ErrnoException;
+ method public static void seteuid(int) throws android.system.ErrnoException;
+ method public static void setgid(int) throws android.system.ErrnoException;
+ method public static int setsid() throws android.system.ErrnoException;
+ method public static void setuid(int) throws android.system.ErrnoException;
+ method public static void shutdown(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+ method public static java.io.FileDescriptor socket(int, int, int) throws android.system.ErrnoException;
+ method public static void socketpair(int, int, int, java.io.FileDescriptor, java.io.FileDescriptor) throws android.system.ErrnoException;
+ method public static android.system.StructStat stat(java.lang.String) throws android.system.ErrnoException;
+ method public static android.system.StructStatVfs statvfs(java.lang.String) throws android.system.ErrnoException;
+ method public static java.lang.String strerror(int);
+ method public static java.lang.String strsignal(int);
+ method public static void symlink(java.lang.String, java.lang.String) throws android.system.ErrnoException;
+ method public static long sysconf(int);
+ method public static void tcdrain(java.io.FileDescriptor) throws android.system.ErrnoException;
+ method public static void tcsendbreak(java.io.FileDescriptor, int) throws android.system.ErrnoException;
+ method public static int umask(int);
+ method public static android.system.StructUtsname uname();
+ method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
+ method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
+ method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
+ method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
+ }
+
+ public final class OsConstants {
+ method public static boolean S_ISBLK(int);
+ method public static boolean S_ISCHR(int);
+ method public static boolean S_ISDIR(int);
+ method public static boolean S_ISFIFO(int);
+ method public static boolean S_ISLNK(int);
+ method public static boolean S_ISREG(int);
+ method public static boolean S_ISSOCK(int);
+ method public static boolean WCOREDUMP(int);
+ method public static int WEXITSTATUS(int);
+ method public static boolean WIFEXITED(int);
+ method public static boolean WIFSIGNALED(int);
+ method public static boolean WIFSTOPPED(int);
+ method public static int WSTOPSIG(int);
+ method public static int WTERMSIG(int);
+ method public static java.lang.String errnoName(int);
+ method public static java.lang.String gaiName(int);
+ field public static final int AF_INET;
+ field public static final int AF_INET6;
+ field public static final int AF_UNIX;
+ field public static final int AF_UNSPEC;
+ field public static final int AI_ADDRCONFIG;
+ field public static final int AI_ALL;
+ field public static final int AI_CANONNAME;
+ field public static final int AI_NUMERICHOST;
+ field public static final int AI_NUMERICSERV;
+ field public static final int AI_PASSIVE;
+ field public static final int AI_V4MAPPED;
+ field public static final int CAP_AUDIT_CONTROL;
+ field public static final int CAP_AUDIT_WRITE;
+ field public static final int CAP_BLOCK_SUSPEND;
+ field public static final int CAP_CHOWN;
+ field public static final int CAP_DAC_OVERRIDE;
+ field public static final int CAP_DAC_READ_SEARCH;
+ field public static final int CAP_FOWNER;
+ field public static final int CAP_FSETID;
+ field public static final int CAP_IPC_LOCK;
+ field public static final int CAP_IPC_OWNER;
+ field public static final int CAP_KILL;
+ field public static final int CAP_LAST_CAP;
+ field public static final int CAP_LEASE;
+ field public static final int CAP_LINUX_IMMUTABLE;
+ field public static final int CAP_MAC_ADMIN;
+ field public static final int CAP_MAC_OVERRIDE;
+ field public static final int CAP_MKNOD;
+ field public static final int CAP_NET_ADMIN;
+ field public static final int CAP_NET_BIND_SERVICE;
+ field public static final int CAP_NET_BROADCAST;
+ field public static final int CAP_NET_RAW;
+ field public static final int CAP_SETFCAP;
+ field public static final int CAP_SETGID;
+ field public static final int CAP_SETPCAP;
+ field public static final int CAP_SETUID;
+ field public static final int CAP_SYSLOG;
+ field public static final int CAP_SYS_ADMIN;
+ field public static final int CAP_SYS_BOOT;
+ field public static final int CAP_SYS_CHROOT;
+ field public static final int CAP_SYS_MODULE;
+ field public static final int CAP_SYS_NICE;
+ field public static final int CAP_SYS_PACCT;
+ field public static final int CAP_SYS_PTRACE;
+ field public static final int CAP_SYS_RAWIO;
+ field public static final int CAP_SYS_RESOURCE;
+ field public static final int CAP_SYS_TIME;
+ field public static final int CAP_SYS_TTY_CONFIG;
+ field public static final int CAP_WAKE_ALARM;
+ field public static final int E2BIG;
+ field public static final int EACCES;
+ field public static final int EADDRINUSE;
+ field public static final int EADDRNOTAVAIL;
+ field public static final int EAFNOSUPPORT;
+ field public static final int EAGAIN;
+ field public static final int EAI_AGAIN;
+ field public static final int EAI_BADFLAGS;
+ field public static final int EAI_FAIL;
+ field public static final int EAI_FAMILY;
+ field public static final int EAI_MEMORY;
+ field public static final int EAI_NODATA;
+ field public static final int EAI_NONAME;
+ field public static final int EAI_OVERFLOW;
+ field public static final int EAI_SERVICE;
+ field public static final int EAI_SOCKTYPE;
+ field public static final int EAI_SYSTEM;
+ field public static final int EALREADY;
+ field public static final int EBADF;
+ field public static final int EBADMSG;
+ field public static final int EBUSY;
+ field public static final int ECANCELED;
+ field public static final int ECHILD;
+ field public static final int ECONNABORTED;
+ field public static final int ECONNREFUSED;
+ field public static final int ECONNRESET;
+ field public static final int EDEADLK;
+ field public static final int EDESTADDRREQ;
+ field public static final int EDOM;
+ field public static final int EDQUOT;
+ field public static final int EEXIST;
+ field public static final int EFAULT;
+ field public static final int EFBIG;
+ field public static final int EHOSTUNREACH;
+ field public static final int EIDRM;
+ field public static final int EILSEQ;
+ field public static final int EINPROGRESS;
+ field public static final int EINTR;
+ field public static final int EINVAL;
+ field public static final int EIO;
+ field public static final int EISCONN;
+ field public static final int EISDIR;
+ field public static final int ELOOP;
+ field public static final int EMFILE;
+ field public static final int EMLINK;
+ field public static final int EMSGSIZE;
+ field public static final int EMULTIHOP;
+ field public static final int ENAMETOOLONG;
+ field public static final int ENETDOWN;
+ field public static final int ENETRESET;
+ field public static final int ENETUNREACH;
+ field public static final int ENFILE;
+ field public static final int ENOBUFS;
+ field public static final int ENODATA;
+ field public static final int ENODEV;
+ field public static final int ENOENT;
+ field public static final int ENOEXEC;
+ field public static final int ENOLCK;
+ field public static final int ENOLINK;
+ field public static final int ENOMEM;
+ field public static final int ENOMSG;
+ field public static final int ENOPROTOOPT;
+ field public static final int ENOSPC;
+ field public static final int ENOSR;
+ field public static final int ENOSTR;
+ field public static final int ENOSYS;
+ field public static final int ENOTCONN;
+ field public static final int ENOTDIR;
+ field public static final int ENOTEMPTY;
+ field public static final int ENOTSOCK;
+ field public static final int ENOTSUP;
+ field public static final int ENOTTY;
+ field public static final int ENXIO;
+ field public static final int EOPNOTSUPP;
+ field public static final int EOVERFLOW;
+ field public static final int EPERM;
+ field public static final int EPIPE;
+ field public static final int EPROTO;
+ field public static final int EPROTONOSUPPORT;
+ field public static final int EPROTOTYPE;
+ field public static final int ERANGE;
+ field public static final int EROFS;
+ field public static final int ESPIPE;
+ field public static final int ESRCH;
+ field public static final int ESTALE;
+ field public static final int ETIME;
+ field public static final int ETIMEDOUT;
+ field public static final int ETXTBSY;
+ field public static final int EXDEV;
+ field public static final int EXIT_FAILURE;
+ field public static final int EXIT_SUCCESS;
+ field public static final int FD_CLOEXEC;
+ field public static final int FIONREAD;
+ field public static final int F_DUPFD;
+ field public static final int F_GETFD;
+ field public static final int F_GETFL;
+ field public static final int F_GETLK;
+ field public static final int F_GETLK64;
+ field public static final int F_GETOWN;
+ field public static final int F_OK;
+ field public static final int F_RDLCK;
+ field public static final int F_SETFD;
+ field public static final int F_SETFL;
+ field public static final int F_SETLK;
+ field public static final int F_SETLK64;
+ field public static final int F_SETLKW;
+ field public static final int F_SETLKW64;
+ field public static final int F_SETOWN;
+ field public static final int F_UNLCK;
+ field public static final int F_WRLCK;
+ field public static final int IFA_F_DADFAILED;
+ field public static final int IFA_F_DEPRECATED;
+ field public static final int IFA_F_HOMEADDRESS;
+ field public static final int IFA_F_NODAD;
+ field public static final int IFA_F_OPTIMISTIC;
+ field public static final int IFA_F_PERMANENT;
+ field public static final int IFA_F_SECONDARY;
+ field public static final int IFA_F_TEMPORARY;
+ field public static final int IFA_F_TENTATIVE;
+ field public static final int IFF_ALLMULTI;
+ field public static final int IFF_AUTOMEDIA;
+ field public static final int IFF_BROADCAST;
+ field public static final int IFF_DEBUG;
+ field public static final int IFF_DYNAMIC;
+ field public static final int IFF_LOOPBACK;
+ field public static final int IFF_MASTER;
+ field public static final int IFF_MULTICAST;
+ field public static final int IFF_NOARP;
+ field public static final int IFF_NOTRAILERS;
+ field public static final int IFF_POINTOPOINT;
+ field public static final int IFF_PORTSEL;
+ field public static final int IFF_PROMISC;
+ field public static final int IFF_RUNNING;
+ field public static final int IFF_SLAVE;
+ field public static final int IFF_UP;
+ field public static final int IPPROTO_ICMP;
+ field public static final int IPPROTO_ICMPV6;
+ field public static final int IPPROTO_IP;
+ field public static final int IPPROTO_IPV6;
+ field public static final int IPPROTO_RAW;
+ field public static final int IPPROTO_TCP;
+ field public static final int IPPROTO_UDP;
+ field public static final int IPV6_CHECKSUM;
+ field public static final int IPV6_MULTICAST_HOPS;
+ field public static final int IPV6_MULTICAST_IF;
+ field public static final int IPV6_MULTICAST_LOOP;
+ field public static final int IPV6_RECVDSTOPTS;
+ field public static final int IPV6_RECVHOPLIMIT;
+ field public static final int IPV6_RECVHOPOPTS;
+ field public static final int IPV6_RECVPKTINFO;
+ field public static final int IPV6_RECVRTHDR;
+ field public static final int IPV6_RECVTCLASS;
+ field public static final int IPV6_TCLASS;
+ field public static final int IPV6_UNICAST_HOPS;
+ field public static final int IPV6_V6ONLY;
+ field public static final int IP_MULTICAST_IF;
+ field public static final int IP_MULTICAST_LOOP;
+ field public static final int IP_MULTICAST_TTL;
+ field public static final int IP_TOS;
+ field public static final int IP_TTL;
+ field public static final int MAP_FIXED;
+ field public static final int MAP_PRIVATE;
+ field public static final int MAP_SHARED;
+ field public static final int MCAST_BLOCK_SOURCE;
+ field public static final int MCAST_JOIN_GROUP;
+ field public static final int MCAST_JOIN_SOURCE_GROUP;
+ field public static final int MCAST_LEAVE_GROUP;
+ field public static final int MCAST_LEAVE_SOURCE_GROUP;
+ field public static final int MCAST_UNBLOCK_SOURCE;
+ field public static final int MCL_CURRENT;
+ field public static final int MCL_FUTURE;
+ field public static final int MSG_CTRUNC;
+ field public static final int MSG_DONTROUTE;
+ field public static final int MSG_EOR;
+ field public static final int MSG_OOB;
+ field public static final int MSG_PEEK;
+ field public static final int MSG_TRUNC;
+ field public static final int MSG_WAITALL;
+ field public static final int MS_ASYNC;
+ field public static final int MS_INVALIDATE;
+ field public static final int MS_SYNC;
+ field public static final int NI_DGRAM;
+ field public static final int NI_NAMEREQD;
+ field public static final int NI_NOFQDN;
+ field public static final int NI_NUMERICHOST;
+ field public static final int NI_NUMERICSERV;
+ field public static final int O_ACCMODE;
+ field public static final int O_APPEND;
+ field public static final int O_CREAT;
+ field public static final int O_EXCL;
+ field public static final int O_NOCTTY;
+ field public static final int O_NOFOLLOW;
+ field public static final int O_NONBLOCK;
+ field public static final int O_RDONLY;
+ field public static final int O_RDWR;
+ field public static final int O_SYNC;
+ field public static final int O_TRUNC;
+ field public static final int O_WRONLY;
+ field public static final int POLLERR;
+ field public static final int POLLHUP;
+ field public static final int POLLIN;
+ field public static final int POLLNVAL;
+ field public static final int POLLOUT;
+ field public static final int POLLPRI;
+ field public static final int POLLRDBAND;
+ field public static final int POLLRDNORM;
+ field public static final int POLLWRBAND;
+ field public static final int POLLWRNORM;
+ field public static final int PROT_EXEC;
+ field public static final int PROT_NONE;
+ field public static final int PROT_READ;
+ field public static final int PROT_WRITE;
+ field public static final int PR_SET_NO_NEW_PRIVS;
+ field public static final int RT_SCOPE_HOST;
+ field public static final int RT_SCOPE_LINK;
+ field public static final int RT_SCOPE_NOWHERE;
+ field public static final int RT_SCOPE_SITE;
+ field public static final int RT_SCOPE_UNIVERSE;
+ field public static final int R_OK;
+ field public static final int SEEK_CUR;
+ field public static final int SEEK_END;
+ field public static final int SEEK_SET;
+ field public static final int SHUT_RD;
+ field public static final int SHUT_RDWR;
+ field public static final int SHUT_WR;
+ field public static final int SIGABRT;
+ field public static final int SIGALRM;
+ field public static final int SIGBUS;
+ field public static final int SIGCHLD;
+ field public static final int SIGCONT;
+ field public static final int SIGFPE;
+ field public static final int SIGHUP;
+ field public static final int SIGILL;
+ field public static final int SIGINT;
+ field public static final int SIGIO;
+ field public static final int SIGKILL;
+ field public static final int SIGPIPE;
+ field public static final int SIGPROF;
+ field public static final int SIGPWR;
+ field public static final int SIGQUIT;
+ field public static final int SIGRTMAX;
+ field public static final int SIGRTMIN;
+ field public static final int SIGSEGV;
+ field public static final int SIGSTKFLT;
+ field public static final int SIGSTOP;
+ field public static final int SIGSYS;
+ field public static final int SIGTERM;
+ field public static final int SIGTRAP;
+ field public static final int SIGTSTP;
+ field public static final int SIGTTIN;
+ field public static final int SIGTTOU;
+ field public static final int SIGURG;
+ field public static final int SIGUSR1;
+ field public static final int SIGUSR2;
+ field public static final int SIGVTALRM;
+ field public static final int SIGWINCH;
+ field public static final int SIGXCPU;
+ field public static final int SIGXFSZ;
+ field public static final int SIOCGIFADDR;
+ field public static final int SIOCGIFBRDADDR;
+ field public static final int SIOCGIFDSTADDR;
+ field public static final int SIOCGIFNETMASK;
+ field public static final int SOCK_DGRAM;
+ field public static final int SOCK_RAW;
+ field public static final int SOCK_SEQPACKET;
+ field public static final int SOCK_STREAM;
+ field public static final int SOL_SOCKET;
+ field public static final int SO_BINDTODEVICE;
+ field public static final int SO_BROADCAST;
+ field public static final int SO_DEBUG;
+ field public static final int SO_DONTROUTE;
+ field public static final int SO_ERROR;
+ field public static final int SO_KEEPALIVE;
+ field public static final int SO_LINGER;
+ field public static final int SO_OOBINLINE;
+ field public static final int SO_PASSCRED;
+ field public static final int SO_PEERCRED;
+ field public static final int SO_RCVBUF;
+ field public static final int SO_RCVLOWAT;
+ field public static final int SO_RCVTIMEO;
+ field public static final int SO_REUSEADDR;
+ field public static final int SO_SNDBUF;
+ field public static final int SO_SNDLOWAT;
+ field public static final int SO_SNDTIMEO;
+ field public static final int SO_TYPE;
+ field public static final int STDERR_FILENO;
+ field public static final int STDIN_FILENO;
+ field public static final int STDOUT_FILENO;
+ field public static final int S_IFBLK;
+ field public static final int S_IFCHR;
+ field public static final int S_IFDIR;
+ field public static final int S_IFIFO;
+ field public static final int S_IFLNK;
+ field public static final int S_IFMT;
+ field public static final int S_IFREG;
+ field public static final int S_IFSOCK;
+ field public static final int S_IRGRP;
+ field public static final int S_IROTH;
+ field public static final int S_IRUSR;
+ field public static final int S_IRWXG;
+ field public static final int S_IRWXO;
+ field public static final int S_IRWXU;
+ field public static final int S_ISGID;
+ field public static final int S_ISUID;
+ field public static final int S_ISVTX;
+ field public static final int S_IWGRP;
+ field public static final int S_IWOTH;
+ field public static final int S_IWUSR;
+ field public static final int S_IXGRP;
+ field public static final int S_IXOTH;
+ field public static final int S_IXUSR;
+ field public static final int TCP_NODELAY;
+ field public static final int WCONTINUED;
+ field public static final int WEXITED;
+ field public static final int WNOHANG;
+ field public static final int WNOWAIT;
+ field public static final int WSTOPPED;
+ field public static final int WUNTRACED;
+ field public static final int W_OK;
+ field public static final int X_OK;
+ field public static final int _SC_2_CHAR_TERM;
+ field public static final int _SC_2_C_BIND;
+ field public static final int _SC_2_C_DEV;
+ field public static final int _SC_2_C_VERSION;
+ field public static final int _SC_2_FORT_DEV;
+ field public static final int _SC_2_FORT_RUN;
+ field public static final int _SC_2_LOCALEDEF;
+ field public static final int _SC_2_SW_DEV;
+ field public static final int _SC_2_UPE;
+ field public static final int _SC_2_VERSION;
+ field public static final int _SC_AIO_LISTIO_MAX;
+ field public static final int _SC_AIO_MAX;
+ field public static final int _SC_AIO_PRIO_DELTA_MAX;
+ field public static final int _SC_ARG_MAX;
+ field public static final int _SC_ASYNCHRONOUS_IO;
+ field public static final int _SC_ATEXIT_MAX;
+ field public static final int _SC_AVPHYS_PAGES;
+ field public static final int _SC_BC_BASE_MAX;
+ field public static final int _SC_BC_DIM_MAX;
+ field public static final int _SC_BC_SCALE_MAX;
+ field public static final int _SC_BC_STRING_MAX;
+ field public static final int _SC_CHILD_MAX;
+ field public static final int _SC_CLK_TCK;
+ field public static final int _SC_COLL_WEIGHTS_MAX;
+ field public static final int _SC_DELAYTIMER_MAX;
+ field public static final int _SC_EXPR_NEST_MAX;
+ field public static final int _SC_FSYNC;
+ field public static final int _SC_GETGR_R_SIZE_MAX;
+ field public static final int _SC_GETPW_R_SIZE_MAX;
+ field public static final int _SC_IOV_MAX;
+ field public static final int _SC_JOB_CONTROL;
+ field public static final int _SC_LINE_MAX;
+ field public static final int _SC_LOGIN_NAME_MAX;
+ field public static final int _SC_MAPPED_FILES;
+ field public static final int _SC_MEMLOCK;
+ field public static final int _SC_MEMLOCK_RANGE;
+ field public static final int _SC_MEMORY_PROTECTION;
+ field public static final int _SC_MESSAGE_PASSING;
+ field public static final int _SC_MQ_OPEN_MAX;
+ field public static final int _SC_MQ_PRIO_MAX;
+ field public static final int _SC_NGROUPS_MAX;
+ field public static final int _SC_NPROCESSORS_CONF;
+ field public static final int _SC_NPROCESSORS_ONLN;
+ field public static final int _SC_OPEN_MAX;
+ field public static final int _SC_PAGESIZE;
+ field public static final int _SC_PAGE_SIZE;
+ field public static final int _SC_PASS_MAX;
+ field public static final int _SC_PHYS_PAGES;
+ field public static final int _SC_PRIORITIZED_IO;
+ field public static final int _SC_PRIORITY_SCHEDULING;
+ field public static final int _SC_REALTIME_SIGNALS;
+ field public static final int _SC_RE_DUP_MAX;
+ field public static final int _SC_RTSIG_MAX;
+ field public static final int _SC_SAVED_IDS;
+ field public static final int _SC_SEMAPHORES;
+ field public static final int _SC_SEM_NSEMS_MAX;
+ field public static final int _SC_SEM_VALUE_MAX;
+ field public static final int _SC_SHARED_MEMORY_OBJECTS;
+ field public static final int _SC_SIGQUEUE_MAX;
+ field public static final int _SC_STREAM_MAX;
+ field public static final int _SC_SYNCHRONIZED_IO;
+ field public static final int _SC_THREADS;
+ field public static final int _SC_THREAD_ATTR_STACKADDR;
+ field public static final int _SC_THREAD_ATTR_STACKSIZE;
+ field public static final int _SC_THREAD_DESTRUCTOR_ITERATIONS;
+ field public static final int _SC_THREAD_KEYS_MAX;
+ field public static final int _SC_THREAD_PRIORITY_SCHEDULING;
+ field public static final int _SC_THREAD_PRIO_INHERIT;
+ field public static final int _SC_THREAD_PRIO_PROTECT;
+ field public static final int _SC_THREAD_SAFE_FUNCTIONS;
+ field public static final int _SC_THREAD_STACK_MIN;
+ field public static final int _SC_THREAD_THREADS_MAX;
+ field public static final int _SC_TIMERS;
+ field public static final int _SC_TIMER_MAX;
+ field public static final int _SC_TTY_NAME_MAX;
+ field public static final int _SC_TZNAME_MAX;
+ field public static final int _SC_VERSION;
+ field public static final int _SC_XBS5_ILP32_OFF32;
+ field public static final int _SC_XBS5_ILP32_OFFBIG;
+ field public static final int _SC_XBS5_LP64_OFF64;
+ field public static final int _SC_XBS5_LPBIG_OFFBIG;
+ field public static final int _SC_XOPEN_CRYPT;
+ field public static final int _SC_XOPEN_ENH_I18N;
+ field public static final int _SC_XOPEN_LEGACY;
+ field public static final int _SC_XOPEN_REALTIME;
+ field public static final int _SC_XOPEN_REALTIME_THREADS;
+ field public static final int _SC_XOPEN_SHM;
+ field public static final int _SC_XOPEN_UNIX;
+ field public static final int _SC_XOPEN_VERSION;
+ field public static final int _SC_XOPEN_XCU_VERSION;
+ }
+
+ public final class StructPollfd {
+ ctor public StructPollfd();
+ field public short events;
+ field public java.io.FileDescriptor fd;
+ field public short revents;
+ field public java.lang.Object userData;
+ }
+
+ public final class StructStat {
+ ctor public StructStat(long, long, int, long, int, int, long, long, long, long, long, long, long);
+ field public final long st_atime;
+ field public final long st_blksize;
+ field public final long st_blocks;
+ field public final long st_ctime;
+ field public final long st_dev;
+ field public final int st_gid;
+ field public final long st_ino;
+ field public final int st_mode;
+ field public final long st_mtime;
+ field public final long st_nlink;
+ field public final long st_rdev;
+ field public final long st_size;
+ field public final int st_uid;
+ }
+
+ public final class StructStatVfs {
+ ctor public StructStatVfs(long, long, long, long, long, long, long, long, long, long, long);
+ field public final long f_bavail;
+ field public final long f_bfree;
+ field public final long f_blocks;
+ field public final long f_bsize;
+ field public final long f_favail;
+ field public final long f_ffree;
+ field public final long f_files;
+ field public final long f_flag;
+ field public final long f_frsize;
+ field public final long f_fsid;
+ field public final long f_namemax;
+ }
+
+ public final class StructUtsname {
+ ctor public StructUtsname(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ field public final java.lang.String machine;
+ field public final java.lang.String nodename;
+ field public final java.lang.String release;
+ field public final java.lang.String sysname;
+ field public final java.lang.String version;
+ }
+
+}
+
package android.telephony {
public final class CellIdentityCdma implements android.os.Parcelable {
@@ -28875,6 +29583,46 @@ package android.util {
method public void previousMonth();
}
+ public final class MutableBoolean {
+ ctor public MutableBoolean(boolean);
+ field public boolean value;
+ }
+
+ public final class MutableByte {
+ ctor public MutableByte(byte);
+ field public byte value;
+ }
+
+ public final class MutableChar {
+ ctor public MutableChar(char);
+ field public char value;
+ }
+
+ public final class MutableDouble {
+ ctor public MutableDouble(double);
+ field public double value;
+ }
+
+ public final class MutableFloat {
+ ctor public MutableFloat(float);
+ field public float value;
+ }
+
+ public final class MutableInt {
+ ctor public MutableInt(int);
+ field public int value;
+ }
+
+ public final class MutableLong {
+ ctor public MutableLong(long);
+ field public long value;
+ }
+
+ public final class MutableShort {
+ ctor public MutableShort(short);
+ field public short value;
+ }
+
public class NoSuchPropertyException extends java.lang.RuntimeException {
ctor public NoSuchPropertyException(java.lang.String);
}
@@ -32548,6 +33296,34 @@ package android.view.inputmethod {
field public static final android.os.Parcelable.Creator CREATOR;
}
+ public final class CursorAnchorInfo implements android.os.Parcelable {
+ ctor public CursorAnchorInfo(android.os.Parcel);
+ method public int describeContents();
+ method public int getCandidatesEnd();
+ method public int getCandidatesStart();
+ method public android.graphics.RectF getCharacterRect(int);
+ method public float getInsertionMarkerBaseline();
+ method public float getInsertionMarkerBottom();
+ method public float getInsertionMarkerHorizontal();
+ method public float getInsertionMarkerTop();
+ method public android.graphics.Matrix getMatrix();
+ method public int getSelectionEnd();
+ method public int getSelectionStart();
+ method public void writeToParcel(android.os.Parcel, int);
+ 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);
+ method public android.view.inputmethod.CursorAnchorInfo build();
+ method public void reset();
+ method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder setCandidateRange(int, int);
+ method public android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder 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);
+ }
+
public class EditorInfo implements android.text.InputType android.os.Parcelable {
ctor public EditorInfo();
method public int describeContents();
@@ -32756,6 +33532,7 @@ package android.view.inputmethod {
method public void toggleSoftInput(int, int);
method public void toggleSoftInputFromWindow(android.os.IBinder, int, int);
method public void updateCursor(android.view.View, int, int, int, int);
+ method public void updateCursorAnchorInfo(android.view.View, android.view.inputmethod.CursorAnchorInfo);
method public void updateExtractedText(android.view.View, int, android.view.inputmethod.ExtractedText);
method public void updateSelection(android.view.View, int, int, int, int);
method public void viewClicked(android.view.View);
@@ -32778,6 +33555,7 @@ package android.view.inputmethod {
method public abstract void finishInput();
method public abstract void toggleSoftInput(int, int);
method public abstract void updateCursor(android.graphics.Rect);
+ method public abstract void updateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo);
method public abstract void updateExtractedText(int, android.view.inputmethod.ExtractedText);
method public abstract void updateSelection(int, int, int, int, int, int);
method public abstract void viewClicked(boolean);
@@ -33027,7 +33805,6 @@ package android.webkit {
method public abstract long getResources();
method public abstract void grant(long);
field public static final long RESOURCE_AUDIO_CAPTURE = 4L; // 0x4L
- field public static final long RESOURCE_GEOLOCATION = 1L; // 0x1L
field public static final long RESOURCE_VIDEO_CAPTURE = 2L; // 0x2L
}
@@ -33618,9 +34395,11 @@ package android.widget {
ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int);
ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int, int);
method public int getKeyProgressIncrement();
+ method public boolean getSplitTrack();
method public android.graphics.drawable.Drawable getThumb();
method public int getThumbOffset();
method public void setKeyProgressIncrement(int);
+ method public void setSplitTrack(boolean);
method public void setThumb(android.graphics.drawable.Drawable);
method public void setThumbOffset(int);
}
@@ -34157,9 +34936,11 @@ package android.widget {
ctor public EdgeEffect(android.content.Context);
method public boolean draw(android.graphics.Canvas);
method public void finish();
+ method public int getMaxHeight();
method public boolean isFinished();
method public void onAbsorb(int);
method public void onPull(float);
+ method public void onPull(float, float);
method public void onRelease();
method public void setSize(int, int);
}
@@ -35423,6 +36204,7 @@ package android.widget {
ctor public Switch(android.content.Context, android.util.AttributeSet);
ctor public Switch(android.content.Context, android.util.AttributeSet, int);
ctor public Switch(android.content.Context, android.util.AttributeSet, int, int);
+ method public boolean getSplitTrack();
method public int getSwitchMinWidth();
method public int getSwitchPadding();
method public java.lang.CharSequence getTextOff();
@@ -35431,6 +36213,7 @@ package android.widget {
method public int getThumbTextPadding();
method public android.graphics.drawable.Drawable getTrackDrawable();
method public void onMeasure(int, int);
+ method public void setSplitTrack(boolean);
method public void setSwitchMinWidth(int);
method public void setSwitchPadding(int);
method public void setSwitchTextAppearance(android.content.Context, int);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 36f9f4b..b4b3c99 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -17,6 +17,7 @@
package android.app;
import android.annotation.NonNull;
+import android.os.PersistableBundle;
import android.transition.Scene;
import android.transition.TransitionManager;
import android.util.ArrayMap;
@@ -922,6 +923,30 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with
+ * the attribute {@link android.R.attr#persistable} set true.
+ *
+ * @param savedInstanceState if the activity is being re-initialized after
+ * previously being shut down then this Bundle contains the data it most
+ * recently supplied in {@link #onSaveInstanceState}.
+ * <b><i>Note: Otherwise it is null.</i></b>
+ * @param persistentState if the activity is being re-initialized after
+ * previously being shut down or powered off then this Bundle contains the data it most
+ * recently supplied to outPersistentState in {@link #onSaveInstanceState}.
+ * <b><i>Note: Otherwise it is null.</i></b>
+ *
+ * @see #onCreate(android.os.Bundle)
+ * @see #onStart
+ * @see #onSaveInstanceState
+ * @see #onRestoreInstanceState
+ * @see #onPostCreate
+ */
+ protected void onCreate(@Nullable Bundle savedInstanceState,
+ @Nullable PersistableBundle persistentState) {
+ onCreate(savedInstanceState);
+ }
+
+ /**
* The hook for {@link ActivityThread} to restore the state of this activity.
*
* Calls {@link #onSaveInstanceState(android.os.Bundle)} and
@@ -935,6 +960,23 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * The hook for {@link ActivityThread} to restore the state of this activity.
+ *
+ * Calls {@link #onSaveInstanceState(android.os.Bundle)} and
+ * {@link #restoreManagedDialogs(android.os.Bundle)}.
+ *
+ * @param savedInstanceState contains the saved state
+ * @param persistentState contains the persistable saved state
+ */
+ final void performRestoreInstanceState(Bundle savedInstanceState,
+ PersistableBundle persistentState) {
+ onRestoreInstanceState(savedInstanceState, persistentState);
+ if (savedInstanceState != null) {
+ restoreManagedDialogs(savedInstanceState);
+ }
+ }
+
+ /**
* This method is called after {@link #onStart} when the activity is
* being re-initialized from a previously saved state, given here in
* <var>savedInstanceState</var>. Most implementations will simply use {@link #onCreate}
@@ -962,7 +1004,34 @@ public class Activity extends ContextThemeWrapper
}
}
}
-
+
+ /**
+ * This is the same as {@link #onRestoreInstanceState(Bundle)} but is called for activities
+ * created with the attribute {@link android.R.attr#persistable}. The {@link
+ * android.os.PersistableBundle} passed came from the restored PersistableBundle first
+ * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}.
+ *
+ * <p>This method is called between {@link #onStart} and
+ * {@link #onPostCreate}.
+ *
+ * <p>If this method is called {@link #onRestoreInstanceState(Bundle)} will not be called.
+ *
+ * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}.
+ * @param persistentState the data most recently supplied in {@link #onSaveInstanceState}.
+ *
+ * @see #onRestoreInstanceState(Bundle)
+ * @see #onCreate
+ * @see #onPostCreate
+ * @see #onResume
+ * @see #onSaveInstanceState
+ */
+ protected void onRestoreInstanceState(Bundle savedInstanceState,
+ PersistableBundle persistentState) {
+ if (savedInstanceState != null) {
+ onRestoreInstanceState(savedInstanceState);
+ }
+ }
+
/**
* Restore the state of any saved managed dialogs.
*
@@ -1039,6 +1108,21 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * This is the same as {@link #onPostCreate(Bundle)} but is called for activities
+ * created with the attribute {@link android.R.attr#persistable}.
+ *
+ * @param savedInstanceState The data most recently supplied in {@link #onSaveInstanceState}
+ * @param persistentState The data caming from the PersistableBundle first
+ * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}.
+ *
+ * @see #onCreate
+ */
+ protected void onPostCreate(@Nullable Bundle savedInstanceState,
+ @Nullable PersistableBundle persistentState) {
+ onPostCreate(savedInstanceState);
+ }
+
+ /**
* Called after {@link #onCreate} &mdash; or after {@link #onRestart} when
* the activity had been stopped, but is now again being displayed to the
* user. It will be followed by {@link #onResume}.
@@ -1194,6 +1278,22 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * The hook for {@link ActivityThread} to save the state of this activity.
+ *
+ * Calls {@link #onSaveInstanceState(android.os.Bundle)}
+ * and {@link #saveManagedDialogs(android.os.Bundle)}.
+ *
+ * @param outState The bundle to save the state to.
+ * @param outPersistentState The bundle to save persistent state to.
+ */
+ final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
+ onSaveInstanceState(outState, outPersistentState);
+ saveManagedDialogs(outState);
+ if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
+ ", " + outPersistentState);
+ }
+
+ /**
* Called to retrieve per-instance state from an activity before being killed
* so that the state can be restored in {@link #onCreate} or
* {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method
@@ -1248,6 +1348,25 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * This is the same as {@link #onSaveInstanceState} but is called for activities
+ * created with the attribute {@link android.R.attr#persistable}. The {@link
+ * android.os.PersistableBundle} passed in will be saved and presented in
+ * {@link #onCreate(Bundle, PersistableBundle)} the first time that this activity
+ * is restarted following the next device reboot.
+ *
+ * @param outState Bundle in which to place your saved state.
+ * @param outPersistentState State which will be saved across reboots.
+ *
+ * @see #onSaveInstanceState(Bundle)
+ * @see #onCreate
+ * @see #onRestoreInstanceState(Bundle, PersistableBundle)
+ * @see #onPause
+ */
+ protected void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
+ onSaveInstanceState(outState);
+ }
+
+ /**
* Save the state of any managed dialogs.
*
* @param outState place to store the saved state.
@@ -3499,13 +3618,15 @@ public class Activity extends ContextThemeWrapper
}
// Get the primary color and update the RecentsActivityValues for this activity
- TypedArray a = getTheme().obtainStyledAttributes(com.android.internal.R.styleable.Theme);
- int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0);
- a.recycle();
- if (colorPrimary != 0) {
- ActivityManager.RecentsActivityValues v = new ActivityManager.RecentsActivityValues();
- v.colorPrimary = colorPrimary;
- setRecentsActivityValues(v);
+ if (theme != null) {
+ TypedArray a = theme.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+ int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0);
+ a.recycle();
+ if (colorPrimary != 0) {
+ ActivityManager.RecentsActivityValues v = new ActivityManager.RecentsActivityValues();
+ v.colorPrimary = colorPrimary;
+ setRecentsActivityValues(v);
+ }
}
}
@@ -4807,6 +4928,7 @@ public class Activity extends ContextThemeWrapper
public void setRecentsActivityValues(ActivityManager.RecentsActivityValues values) {
ActivityManager.RecentsActivityValues activityValues =
new ActivityManager.RecentsActivityValues(values);
+ // Scale the icon down to something reasonable
if (values.icon != null) {
final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
activityValues.icon = Bitmap.createScaledBitmap(values.icon, size, size, true);
@@ -5488,13 +5610,22 @@ public class Activity extends ContextThemeWrapper
return mParent != null ? mParent.getActivityToken() : mToken;
}
- final void performCreate(Bundle icicle) {
- onCreate(icicle);
+ final void performCreateCommon() {
mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
mFragments.dispatchActivityCreated();
}
+ final void performCreate(Bundle icicle) {
+ onCreate(icicle);
+ performCreateCommon();
+ }
+
+ final void performCreate(Bundle icicle, PersistableBundle persistentState) {
+ onCreate(icicle, persistentState);
+ performCreateCommon();
+ }
+
final void performStart() {
mFragments.noteStateNotSaved();
mCalled = false;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 5d809d8..044727d 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -20,7 +20,6 @@ import android.os.BatteryStats;
import android.os.IBinder;
import com.android.internal.app.IUsageStats;
import com.android.internal.app.ProcessStats;
-import com.android.internal.os.PkgUsageStats;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.FastPrintWriter;
@@ -2130,14 +2129,15 @@ public class ActivityManager {
return new HashMap<String, Integer>();
}
- PkgUsageStats[] allPkgUsageStats = usageStatsService.getAllPkgUsageStats();
+ UsageStats.PackageStats[] allPkgUsageStats = usageStatsService.getAllPkgUsageStats(
+ ActivityThread.currentPackageName());
if (allPkgUsageStats == null) {
return new HashMap<String, Integer>();
}
Map<String, Integer> launchCounts = new HashMap<String, Integer>();
- for (PkgUsageStats pkgUsageStats : allPkgUsageStats) {
- launchCounts.put(pkgUsageStats.packageName, pkgUsageStats.launchCount);
+ for (UsageStats.PackageStats pkgUsageStats : allPkgUsageStats) {
+ launchCounts.put(pkgUsageStats.getPackageName(), pkgUsageStats.getLaunchCount());
}
return launchCounts;
@@ -2251,17 +2251,17 @@ public class ActivityManager {
*
* @hide
*/
- public PkgUsageStats[] getAllPackageUsageStats() {
+ public UsageStats.PackageStats[] getAllPackageUsageStats() {
try {
IUsageStats usageStatsService = IUsageStats.Stub.asInterface(
ServiceManager.getService("usagestats"));
if (usageStatsService != null) {
- return usageStatsService.getAllPkgUsageStats();
+ return usageStatsService.getAllPkgUsageStats(ActivityThread.currentPackageName());
}
} catch (RemoteException e) {
Log.w(TAG, "Could not query usage stats", e);
}
- return new PkgUsageStats[0];
+ return new UsageStats.PackageStats[0];
}
/**
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 57da21e..ec2868a 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -40,6 +40,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
@@ -454,7 +455,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case ACTIVITY_PAUSED_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
- activityPaused(token);
+ PersistableBundle persistentState = data.readPersistableBundle();
+ activityPaused(token, persistentState);
reply.writeNoException();
return true;
}
@@ -463,10 +465,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
Bundle map = data.readBundle();
- Bitmap thumbnail = data.readInt() != 0
- ? Bitmap.CREATOR.createFromParcel(data) : null;
+ PersistableBundle persistentState = data.readPersistableBundle();
CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
- activityStopped(token, map, thumbnail, description);
+ activityStopped(token, map, persistentState, description);
reply.writeNoException();
return true;
}
@@ -2583,31 +2584,27 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
reply.recycle();
}
- public void activityPaused(IBinder token) throws RemoteException
+ public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
+ data.writePersistableBundle(persistentState);
mRemote.transact(ACTIVITY_PAUSED_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
public void activityStopped(IBinder token, Bundle state,
- Bitmap thumbnail, CharSequence description) throws RemoteException
+ PersistableBundle persistentState, CharSequence description) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
data.writeBundle(state);
- if (thumbnail != null) {
- data.writeInt(1);
- thumbnail.writeToParcel(data, 0);
- } else {
- data.writeInt(0);
- }
+ data.writePersistableBundle(persistentState);
TextUtils.writeToParcel(description, data, 0);
mRemote.transact(ACTIVITY_STOPPED_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
reply.readException();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b606088..161cb76 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -56,11 +56,11 @@ import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
-import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -69,8 +69,6 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
-import android.transition.Scene;
-import android.transition.TransitionManager;
import android.provider.Settings;
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
@@ -268,6 +266,7 @@ public final class ActivityThread {
Intent intent;
IVoiceInteractor voiceInteractor;
Bundle state;
+ PersistableBundle persistentState;
Activity activity;
Window window;
Activity parent;
@@ -317,6 +316,10 @@ public final class ActivityThread {
return false;
}
+ public boolean isPersistable() {
+ return (activityInfo.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+ }
+
public String toString() {
ComponentName componentName = intent != null ? intent.getComponent() : null;
return "ActivityRecord{"
@@ -605,8 +608,8 @@ public final class ActivityThread {
// activity itself back to the activity manager. (matters more with ipc)
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
- IVoiceInteractor voiceInteractor,
- int procState, Bundle state, List<ResultInfo> pendingResults,
+ IVoiceInteractor voiceInteractor, int procState, Bundle state,
+ PersistableBundle persistentState, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
Bundle resumeArgs) {
@@ -622,6 +625,7 @@ public final class ActivityThread {
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
+ r.persistentState = persistentState;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
@@ -2205,7 +2209,11 @@ public final class ActivityThread {
}
activity.mCalled = false;
- mInstrumentation.callActivityOnCreate(activity, r.state);
+ if (r.isPersistable()) {
+ mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
+ } else {
+ mInstrumentation.callActivityOnCreate(activity, r.state);
+ }
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
@@ -2218,13 +2226,23 @@ public final class ActivityThread {
r.stopped = false;
}
if (!r.activity.mFinished) {
- if (r.state != null) {
+ if (r.isPersistable()) {
+ if (r.state != null || r.persistentState != null) {
+ mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
+ r.persistentState);
+ }
+ } else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
- mInstrumentation.callActivityOnPostCreate(activity, r.state);
+ if (r.isPersistable()) {
+ mInstrumentation.callActivityOnPostCreate(activity, r.state,
+ r.persistentState);
+ } else {
+ mInstrumentation.callActivityOnPostCreate(activity, r.state);
+ }
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
@@ -2842,6 +2860,7 @@ public final class ActivityThread {
r.paused = false;
r.stopped = false;
r.state = null;
+ r.persistentState = null;
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
throw new RuntimeException(
@@ -3069,7 +3088,7 @@ public final class ActivityThread {
// Tell the activity manager we have paused.
try {
- ActivityManagerNative.getDefault().activityPaused(token);
+ ActivityManagerNative.getDefault().activityPaused(token, r.persistentState);
} catch (RemoteException ex) {
}
}
@@ -3099,17 +3118,13 @@ public final class ActivityThread {
+ r.intent.getComponent().toShortString());
Slog.e(TAG, e.getMessage(), e);
}
- Bundle state = null;
if (finished) {
r.activity.mFinished = true;
}
try {
// Next have the activity save its current state and managed dialogs...
if (!r.activity.mFinished && saveState) {
- state = new Bundle();
- state.setAllowFds(false);
- mInstrumentation.callActivityOnSaveInstanceState(r.activity, state);
- r.state = state;
+ callCallActivityOnSaveInstanceState(r);
}
// Now we are idle.
r.activity.mCalled = false;
@@ -3145,7 +3160,7 @@ public final class ActivityThread {
listeners.get(i).onPaused(r.activity);
}
- return state;
+ return !r.activity.mFinished && saveState ? r.state : null;
}
final void performStopActivity(IBinder token, boolean saveState) {
@@ -3156,7 +3171,7 @@ public final class ActivityThread {
private static class StopInfo implements Runnable {
ActivityClientRecord activity;
Bundle state;
- Bitmap thumbnail;
+ PersistableBundle persistentState;
CharSequence description;
@Override public void run() {
@@ -3164,7 +3179,7 @@ public final class ActivityThread {
try {
if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + activity);
ActivityManagerNative.getDefault().activityStopped(
- activity.token, state, thumbnail, description);
+ activity.token, state, persistentState, description);
} catch (RemoteException ex) {
}
}
@@ -3203,7 +3218,6 @@ public final class ActivityThread {
private void performStopActivityInner(ActivityClientRecord r,
StopInfo info, boolean keepShown, boolean saveState) {
if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
- Bundle state = null;
if (r != null) {
if (!keepShown && r.stopped) {
if (r.activity.mFinished) {
@@ -3223,7 +3237,6 @@ public final class ActivityThread {
// First create a thumbnail for the activity...
// For now, don't create the thumbnail here; we are
// doing that by doing a screen snapshot.
- info.thumbnail = null; //createThumbnailBitmap(r);
info.description = r.activity.onCreateDescription();
} catch (Exception e) {
if (!mInstrumentation.onException(r.activity, e)) {
@@ -3238,12 +3251,7 @@ public final class ActivityThread {
// Next have the activity save its current state and managed dialogs...
if (!r.activity.mFinished && saveState) {
if (r.state == null) {
- state = new Bundle();
- state.setAllowFds(false);
- mInstrumentation.callActivityOnSaveInstanceState(r.activity, state);
- r.state = state;
- } else {
- state = r.state;
+ callCallActivityOnSaveInstanceState(r);
}
}
@@ -3319,6 +3327,7 @@ public final class ActivityThread {
// manager to proceed and allow us to go fully into the background.
info.activity = r;
info.state = r.state;
+ info.persistentState = r.persistentState;
mH.post(info);
}
@@ -3775,9 +3784,7 @@ public final class ActivityThread {
performPauseActivity(r.token, false, r.isPreHoneycomb());
}
if (r.state == null && !r.stopped && !r.isPreHoneycomb()) {
- r.state = new Bundle();
- r.state.setAllowFds(false);
- mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
+ callCallActivityOnSaveInstanceState(r);
}
handleDestroyActivity(r.token, false, configChanges, true);
@@ -3807,6 +3814,18 @@ public final class ActivityThread {
handleLaunchActivity(r, currentIntent);
}
+ private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
+ r.state = new Bundle();
+ r.state.setAllowFds(false);
+ if (r.isPersistable()) {
+ r.persistentState = new PersistableBundle();
+ mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
+ r.persistentState);
+ } else {
+ mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
+ }
+ }
+
ArrayList<ComponentCallbacks2> collectComponentCallbacks(
boolean allActivities, Configuration newConfig) {
ArrayList<ComponentCallbacks2> callbacks
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index b616c1e..d813dab 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -16,6 +16,7 @@
package android.app;
+import android.Manifest;
import android.os.Binder;
import android.os.IBinder;
import android.util.ArrayMap;
@@ -184,8 +185,10 @@ public class AppOpsManager {
public static final int OP_MONITOR_LOCATION = 41;
/** @hide Continually monitoring location data with a relatively high power request. */
public static final int OP_MONITOR_HIGH_POWER_LOCATION = 42;
+ /** @hide Retrieve current usage stats via {@link UsageStatsManager}. */
+ public static final int OP_GET_USAGE_STATS = 43;
/** @hide */
- public static final int _NUM_OP = 43;
+ public static final int _NUM_OP = 44;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION =
@@ -252,6 +255,7 @@ public class AppOpsManager {
OP_WAKE_LOCK,
OP_COARSE_LOCATION,
OP_COARSE_LOCATION,
+ OP_GET_USAGE_STATS,
};
/**
@@ -302,6 +306,7 @@ public class AppOpsManager {
null,
OPSTR_MONITOR_LOCATION,
OPSTR_MONITOR_HIGH_POWER_LOCATION,
+ null,
};
/**
@@ -352,6 +357,7 @@ public class AppOpsManager {
"WAKE_LOCK",
"MONITOR_LOCATION",
"MONITOR_HIGH_POWER_LOCATION",
+ "GET_USAGE_STATS"
};
/**
@@ -402,6 +408,7 @@ public class AppOpsManager {
android.Manifest.permission.WAKE_LOCK,
null, // no permission for generic location monitoring
null, // no permission for high power location monitoring
+ android.Manifest.permission.PACKAGE_USAGE_STATS,
};
/**
@@ -451,6 +458,7 @@ public class AppOpsManager {
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_IGNORED, // OP_GET_USAGE_STATS
};
/**
@@ -504,6 +512,7 @@ public class AppOpsManager {
false,
false,
false,
+ false,
};
private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 7f2fb59..e7902a9 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -29,6 +29,7 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Debug;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.Parcel;
@@ -141,6 +142,7 @@ public abstract class ApplicationThreadNative extends Binder
data.readStrongBinder());
int procState = data.readInt();
Bundle state = data.readBundle();
+ PersistableBundle persistentState = data.readPersistableBundle();
List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
boolean notResumed = data.readInt() != 0;
@@ -151,9 +153,9 @@ public abstract class ApplicationThreadNative extends Binder
boolean autoStopProfiler = data.readInt() != 0;
Bundle resumeArgs = data.readBundle();
scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo,
- voiceInteractor, procState, state,
- ri, pi, notResumed, isForward, profileName, profileFd, autoStopProfiler,
- resumeArgs);
+ voiceInteractor, procState, state, persistentState,
+ ri, pi, notResumed, isForward, profileName, profileFd,
+ autoStopProfiler, resumeArgs);
return true;
}
@@ -731,8 +733,8 @@ class ApplicationThreadProxy implements IApplicationThread {
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
- IVoiceInteractor voiceInteractor,
- int procState, Bundle state, List<ResultInfo> pendingResults,
+ IVoiceInteractor voiceInteractor, int procState, Bundle state,
+ PersistableBundle persistentState, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
Bundle resumeArgs)
@@ -748,6 +750,7 @@ class ApplicationThreadProxy implements IApplicationThread {
data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null);
data.writeInt(procState);
data.writeBundle(state);
+ data.writePersistableBundle(persistentState);
data.writeTypedList(pendingResults);
data.writeTypedList(pendingNewIntents);
data.writeInt(notResumed ? 1 : 0);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5ed5030..801182d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -678,6 +678,11 @@ class ContextImpl extends Context {
return new NetworkScoreManager(ctx);
}
});
+
+ registerService(USAGE_STATS_SERVICE, new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ return new UsageStatsManager(ctx.getOuterContext());
+ }});
}
static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 2e9cdf3b7..074b427 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -45,6 +45,7 @@ import android.os.IInterface;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.StrictMode;
import android.service.voice.IVoiceInteractionSession;
@@ -104,9 +105,9 @@ public interface IActivityManager extends IInterface {
public void activityResumed(IBinder token) throws RemoteException;
public void activityIdle(IBinder token, Configuration config,
boolean stopProfiling) throws RemoteException;
- public void activityPaused(IBinder token) throws RemoteException;
+ public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException;
public void activityStopped(IBinder token, Bundle state,
- Bitmap thumbnail, CharSequence description) throws RemoteException;
+ PersistableBundle persistentState, CharSequence description) throws RemoteException;
public void activitySlept(IBinder token) throws RemoteException;
public void activityDestroyed(IBinder token) throws RemoteException;
public String getCallingPackage(IBinder token) throws RemoteException;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index fefba8a..a832034 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -28,6 +28,7 @@ import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Debug;
import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.IInterface;
@@ -58,8 +59,8 @@ public interface IApplicationThread extends IInterface {
void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
IVoiceInteractor voiceInteractor, int procState, Bundle state,
- List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed,
- boolean isForward,
+ PersistableBundle persistentState, List<ResultInfo> pendingResults,
+ List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
Bundle resumeArgs)
throws RemoteException;
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 347de97..8ab9ac3 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -43,4 +43,5 @@ interface IUiAutomationConnection {
WindowContentFrameStats getWindowContentFrameStats(int windowId);
void clearWindowAnimationFrameStats();
WindowAnimationFrameStats getWindowAnimationFrameStats();
+ void executeShellCommand(String command, in ParcelFileDescriptor fd);
}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e58ccb8..bb3e002 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -30,6 +30,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.MessageQueue;
import android.os.PerformanceCollector;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -1061,15 +1062,7 @@ public class Instrumentation {
return (Activity)cl.loadClass(className).newInstance();
}
- /**
- * Perform calling of an activity's {@link Activity#onCreate}
- * method. The default implementation simply calls through to that method.
- *
- * @param activity The activity being created.
- * @param icicle The previously frozen state (or null) to pass through to
- * onCreate().
- */
- public void callActivityOnCreate(Activity activity, Bundle icicle) {
+ private void prePerformCreate(Activity activity) {
if (mWaitingActivities != null) {
synchronized (mSync) {
final int N = mWaitingActivities.size();
@@ -1083,9 +1076,9 @@ public class Instrumentation {
}
}
}
-
- activity.performCreate(icicle);
-
+ }
+
+ private void postPerformCreate(Activity activity) {
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
@@ -1096,6 +1089,33 @@ public class Instrumentation {
}
}
}
+
+ /**
+ * Perform calling of an activity's {@link Activity#onCreate}
+ * method. The default implementation simply calls through to that method.
+ *
+ * @param activity The activity being created.
+ * @param icicle The previously frozen state (or null) to pass through to onCreate().
+ */
+ public void callActivityOnCreate(Activity activity, Bundle icicle) {
+ prePerformCreate(activity);
+ activity.performCreate(icicle);
+ postPerformCreate(activity);
+ }
+
+ /**
+ * Perform calling of an activity's {@link Activity#onCreate}
+ * method. The default implementation simply calls through to that method.
+ * @param activity The activity being created.
+ * @param icicle The previously frozen state (or null) to pass through to
+ * @param persistentState The previously persisted state (or null)
+ */
+ public void callActivityOnCreate(Activity activity, Bundle icicle,
+ PersistableBundle persistentState) {
+ prePerformCreate(activity);
+ activity.performCreate(icicle, persistentState);
+ postPerformCreate(activity);
+ }
public void callActivityOnDestroy(Activity activity) {
// TODO: the following block causes intermittent hangs when using startActivity
@@ -1130,7 +1150,7 @@ public class Instrumentation {
/**
* Perform calling of an activity's {@link Activity#onRestoreInstanceState}
* method. The default implementation simply calls through to that method.
- *
+ *
* @param activity The activity being restored.
* @param savedInstanceState The previously saved state being restored.
*/
@@ -1139,9 +1159,22 @@ public class Instrumentation {
}
/**
+ * Perform calling of an activity's {@link Activity#onRestoreInstanceState}
+ * method. The default implementation simply calls through to that method.
+ *
+ * @param activity The activity being restored.
+ * @param savedInstanceState The previously saved state being restored.
+ * @param persistentState The previously persisted state (or null)
+ */
+ public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState,
+ PersistableBundle persistentState) {
+ activity.performRestoreInstanceState(savedInstanceState, persistentState);
+ }
+
+ /**
* Perform calling of an activity's {@link Activity#onPostCreate} method.
* The default implementation simply calls through to that method.
- *
+ *
* @param activity The activity being created.
* @param icicle The previously frozen state (or null) to pass through to
* onPostCreate().
@@ -1151,6 +1184,19 @@ public class Instrumentation {
}
/**
+ * Perform calling of an activity's {@link Activity#onPostCreate} method.
+ * The default implementation simply calls through to that method.
+ *
+ * @param activity The activity being created.
+ * @param icicle The previously frozen state (or null) to pass through to
+ * onPostCreate().
+ */
+ public void callActivityOnPostCreate(Activity activity, Bundle icicle,
+ PersistableBundle persistentState) {
+ activity.onPostCreate(icicle, persistentState);
+ }
+
+ /**
* Perform calling of an activity's {@link Activity#onNewIntent}
* method. The default implementation simply calls through to that method.
*
@@ -1215,7 +1261,7 @@ public class Instrumentation {
/**
* Perform calling of an activity's {@link Activity#onSaveInstanceState}
* method. The default implementation simply calls through to that method.
- *
+ *
* @param activity The activity being saved.
* @param outState The bundle to pass to the call.
*/
@@ -1224,6 +1270,18 @@ public class Instrumentation {
}
/**
+ * Perform calling of an activity's {@link Activity#onSaveInstanceState}
+ * method. The default implementation simply calls through to that method.
+ * @param activity The activity being saved.
+ * @param outState The bundle to pass to the call.
+ * @param outPersistentState The persistent bundle to pass to the call.
+ */
+ public void callActivityOnSaveInstanceState(Activity activity, Bundle outState,
+ PersistableBundle outPersistentState) {
+ activity.performSaveInstanceState(outState, outPersistentState);
+ }
+
+ /**
* Perform calling of an activity's {@link Activity#onPause} method. The
* default implementation simply calls through to that method.
*
@@ -1428,7 +1486,7 @@ public class Instrumentation {
}
/**
- * Like {@link #execStartActivity},
+ * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)},
* but accepts an array of activities to be started. Note that active
* {@link ActivityMonitor} objects only match against the first activity in
* the array.
@@ -1442,7 +1500,7 @@ public class Instrumentation {
}
/**
- * Like {@link #execStartActivity},
+ * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)},
* but accepts an array of activities to be started. Note that active
* {@link ActivityMonitor} objects only match against the first activity in
* the array.
@@ -1545,7 +1603,8 @@ public class Instrumentation {
}
/**
- * Like {@link #execStartActivity}, but for starting as a particular user.
+ * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)},
+ * but for starting as a particular user.
*
* @param who The Context from which the activity is being started.
* @param contextThread The main thread of the Context from which the activity
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index aab6ed8..db91742a 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -44,8 +44,8 @@ public class KeyguardManager {
* you to disable / reenable the keyguard.
*/
public class KeyguardLock {
- private IBinder mToken = new Binder();
- private String mTag;
+ private final IBinder mToken = new Binder();
+ private final String mTag;
KeyguardLock(String tag) {
mTag = tag;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index bba6caf..76a6a8e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -659,8 +659,8 @@ public class Notification implements Parcelable
/**
* @hide
- * Extra added by NotificationManagerService to indicate whether a NotificationScorer
- * modified the Notifications's score.
+ * Extra added by NotificationManagerService to indicate whether
+ * the Notifications's score has been modified.
*/
public static final String EXTRA_SCORE_MODIFIED = "android.scoreModified";
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 9405325..64e3484 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -26,6 +26,7 @@ import android.graphics.Canvas;
import android.graphics.Point;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Looper;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
@@ -40,7 +41,9 @@ import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
+import libcore.io.IoUtils;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;
@@ -840,6 +843,44 @@ public final class UiAutomation {
return null;
}
+ /**
+ * Executes a shell command. This method returs a file descriptor that points
+ * to the standard output stream. The command execution is similar to running
+ * "adb shell <command>" from a host connected to the device.
+ * <p>
+ * <strong>Note:</strong> It is your responsibility to close the retunred file
+ * descriptor once you are done reading.
+ * </p>
+ *
+ * @param command The command to execute.
+ * @return A file descriptor to the standard output stream.
+ */
+ public ParcelFileDescriptor executeShellCommand(String command) {
+ synchronized (mLock) {
+ throwIfNotConnectedLocked();
+ }
+
+ ParcelFileDescriptor source = null;
+ ParcelFileDescriptor sink = null;
+
+ try {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ source = pipe[0];
+ sink = pipe[1];
+
+ // Calling out without a lock held.
+ mUiAutomationConnection.executeShellCommand(command, sink);
+ } catch (IOException ioe) {
+ Log.e(LOG_TAG, "Error executing shell command!", ioe);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error executing shell command!", re);
+ } finally {
+ IoUtils.closeQuietly(sink);
+ }
+
+ return source;
+ }
+
private static float getDegreesForRotation(int value) {
switch (value) {
case Surface.ROTATION_90: {
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index fa40286..81bcb39 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -23,6 +23,7 @@ import android.graphics.Bitmap;
import android.hardware.input.InputManager;
import android.os.Binder;
import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -33,6 +34,12 @@ import android.view.WindowAnimationFrameStats;
import android.view.WindowContentFrameStats;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.IAccessibilityManager;
+import libcore.io.IoUtils;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
/**
* This is a remote object that is passed from the shell to an instrumentation
@@ -50,8 +57,8 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Service.WINDOW_SERVICE));
- private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub.asInterface(
- ServiceManager.getService(Service.ACCESSIBILITY_SERVICE));
+ private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub
+ .asInterface(ServiceManager.getService(Service.ACCESSIBILITY_SERVICE));
private final Object mLock = new Object();
@@ -220,6 +227,41 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
}
@Override
+ public void executeShellCommand(String command, ParcelFileDescriptor sink)
+ throws RemoteException {
+ synchronized (mLock) {
+ throwIfCalledByNotTrustedUidLocked();
+ throwIfShutdownLocked();
+ throwIfNotConnectedLocked();
+ }
+
+ InputStream in = null;
+ OutputStream out = null;
+
+ try {
+ java.lang.Process process = Runtime.getRuntime().exec(command);
+
+ in = process.getInputStream();
+ out = new FileOutputStream(sink.getFileDescriptor());
+
+ 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 ioe) {
+ throw new RuntimeException("Error running shell command", ioe);
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(sink);
+ }
+ }
+
+ @Override
public void shutdown() {
synchronized (mLock) {
if (isConnectedLocked()) {
diff --git a/core/java/android/app/UsageStats.aidl b/core/java/android/app/UsageStats.aidl
new file mode 100644
index 0000000..7dee70a
--- /dev/null
+++ b/core/java/android/app/UsageStats.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.app;
+
+parcelable UsageStats;
+parcelable UsageStats.PackageStats;
diff --git a/core/java/android/app/UsageStats.java b/core/java/android/app/UsageStats.java
new file mode 100644
index 0000000..0aeba59
--- /dev/null
+++ b/core/java/android/app/UsageStats.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.res.Configuration;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+
+import java.util.Map;
+
+/**
+ * Snapshot of current usage stats data.
+ * @hide
+ */
+public class UsageStats implements Parcelable {
+ /** @hide */
+ public final ArrayMap<String, PackageStats> mPackages = new ArrayMap<String, PackageStats>();
+ /** @hide */
+ public final ArrayMap<Configuration, ConfigurationStats> mConfigurations
+ = new ArrayMap<Configuration, ConfigurationStats>();
+
+ public static class PackageStats implements Parcelable {
+ private final String mPackageName;
+ private int mLaunchCount;
+ private long mUsageTime;
+ private long mResumedTime;
+
+ /** @hide */
+ public final ArrayMap<String, Long> componentResumeTimes;
+
+ public static final Parcelable.Creator<PackageStats> CREATOR
+ = new Parcelable.Creator<PackageStats>() {
+ public PackageStats createFromParcel(Parcel in) {
+ return new PackageStats(in);
+ }
+
+ public PackageStats[] newArray(int size) {
+ return new PackageStats[size];
+ }
+ };
+
+ public String toString() {
+ return "PackageStats{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + mPackageName + "}";
+ }
+
+ /** @hide */
+ public PackageStats(String pkgName) {
+ mPackageName = pkgName;
+ componentResumeTimes = new ArrayMap<String, Long>();
+ }
+
+ /** @hide */
+ public PackageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) {
+ mPackageName = pkgName;
+ mLaunchCount = count;
+ mUsageTime = time;
+ componentResumeTimes = new ArrayMap<String, Long>();
+ componentResumeTimes.putAll(lastResumeTimes);
+ }
+
+ /** @hide */
+ public PackageStats(Parcel source) {
+ mPackageName = source.readString();
+ mLaunchCount = source.readInt();
+ mUsageTime = source.readLong();
+ final int N = source.readInt();
+ componentResumeTimes = new ArrayMap<String, Long>(N);
+ for (int i = 0; i < N; i++) {
+ String component = source.readString();
+ long lastResumeTime = source.readLong();
+ componentResumeTimes.put(component, lastResumeTime);
+ }
+ }
+
+ /** @hide */
+ public PackageStats(PackageStats pStats) {
+ mPackageName = pStats.mPackageName;
+ mLaunchCount = pStats.mLaunchCount;
+ mUsageTime = pStats.mUsageTime;
+ componentResumeTimes = new ArrayMap<String, Long>(pStats.componentResumeTimes);
+ }
+
+ /** @hide */
+ public void resume(boolean launched) {
+ if (launched) {
+ mLaunchCount++;
+ }
+ mResumedTime = SystemClock.elapsedRealtime();
+ }
+
+ /** @hide */
+ public void pause() {
+ if (mResumedTime > 0) {
+ mUsageTime += SystemClock.elapsedRealtime() - mResumedTime;
+ }
+ mResumedTime = 0;
+ }
+
+ public final String getPackageName() {
+ return mPackageName;
+ }
+
+ public final long getUsageTime(long elapsedRealtime) {
+ return mUsageTime + (mResumedTime > 0 ? (elapsedRealtime- mResumedTime) : 0);
+ }
+
+ public final int getLaunchCount() {
+ return mLaunchCount;
+ }
+
+ /** @hide */
+ public boolean clearUsageTimes() {
+ mLaunchCount = 0;
+ mUsageTime = 0;
+ return mResumedTime <= 0 && componentResumeTimes.isEmpty();
+ }
+
+ public final int describeContents() {
+ return 0;
+ }
+
+ public final void writeToParcel(Parcel dest, int parcelableFlags) {
+ writeToParcel(dest, parcelableFlags, 0);
+ }
+
+ final void writeToParcel(Parcel dest, int parcelableFlags, long elapsedRealtime) {
+ dest.writeString(mPackageName);
+ dest.writeInt(mLaunchCount);
+ dest.writeLong(elapsedRealtime > 0 ? getUsageTime(elapsedRealtime) : mUsageTime);
+ dest.writeInt(componentResumeTimes.size());
+ for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) {
+ dest.writeString(ent.getKey());
+ dest.writeLong(ent.getValue());
+ }
+ }
+
+ /** @hide */
+ public void writeExtendedToParcel(Parcel dest, int parcelableFlags) {
+ }
+ }
+
+ public static class ConfigurationStats implements Parcelable {
+ private final Configuration mConfiguration;
+ private long mLastUsedTime;
+ private int mUsageCount;
+ private long mUsageTime;
+ private long mStartedTime;
+
+ public static final Parcelable.Creator<ConfigurationStats> CREATOR
+ = new Parcelable.Creator<ConfigurationStats>() {
+ public ConfigurationStats createFromParcel(Parcel in) {
+ return new ConfigurationStats(in);
+ }
+
+ public ConfigurationStats[] newArray(int size) {
+ return new ConfigurationStats[size];
+ }
+ };
+
+ public String toString() {
+ return "ConfigurationStats{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + mConfiguration + "}";
+ }
+
+ /** @hide */
+ public ConfigurationStats(Configuration config) {
+ mConfiguration = config;
+ }
+
+ /** @hide */
+ public ConfigurationStats(Parcel source) {
+ mConfiguration = Configuration.CREATOR.createFromParcel(source);
+ mLastUsedTime = source.readLong();
+ mUsageCount = source.readInt();
+ mUsageTime = source.readLong();
+ }
+
+ /** @hide */
+ public ConfigurationStats(ConfigurationStats pStats) {
+ mConfiguration = pStats.mConfiguration;
+ mLastUsedTime = pStats.mLastUsedTime;
+ mUsageCount = pStats.mUsageCount;
+ mUsageTime = pStats.mUsageTime;
+ }
+
+ public final Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ public final long getLastUsedTime() {
+ return mLastUsedTime;
+ }
+
+ public final long getUsageTime(long elapsedRealtime) {
+ return mUsageTime + (mStartedTime > 0 ? (elapsedRealtime- mStartedTime) : 0);
+ }
+
+ public final int getUsageCount() {
+ return mUsageCount;
+ }
+
+ /** @hide */
+ public void start() {
+ mLastUsedTime = System.currentTimeMillis();
+ mUsageCount++;
+ mStartedTime = SystemClock.elapsedRealtime();
+ }
+
+ /** @hide */
+ public void stop() {
+ if (mStartedTime > 0) {
+ mUsageTime += SystemClock.elapsedRealtime() - mStartedTime;
+ }
+ mStartedTime = 0;
+ }
+
+ /** @hide */
+ public boolean clearUsageTimes() {
+ mUsageCount = 0;
+ mUsageTime = 0;
+ return mLastUsedTime == 0 && mStartedTime <= 0;
+ }
+
+ public final int describeContents() {
+ return 0;
+ }
+
+ public final void writeToParcel(Parcel dest, int parcelableFlags) {
+ writeToParcel(dest, parcelableFlags, 0);
+ }
+
+ final void writeToParcel(Parcel dest, int parcelableFlags, long elapsedRealtime) {
+ mConfiguration.writeToParcel(dest, parcelableFlags);
+ dest.writeLong(mLastUsedTime);
+ dest.writeInt(mUsageCount);
+ dest.writeLong(elapsedRealtime > 0 ? getUsageTime(elapsedRealtime) : mUsageTime);
+ }
+
+ /** @hide */
+ public void writeExtendedToParcel(Parcel dest, int parcelableFlags) {
+ }
+ }
+
+ /** @hide */
+ public UsageStats() {
+ }
+
+ /** @hide */
+ public UsageStats(Parcel source, boolean extended) {
+ int N = source.readInt();
+ for (int i=0; i<N; i++) {
+ PackageStats pkg = extended ? onNewPackageStats(source) : new PackageStats(source);
+ mPackages.put(pkg.getPackageName(), pkg);
+ }
+ N = source.readInt();
+ for (int i=0; i<N; i++) {
+ ConfigurationStats config = extended ? onNewConfigurationStats(source)
+ : new ConfigurationStats(source);
+ mConfigurations.put(config.getConfiguration(), config);
+ }
+ }
+
+ public int getPackageStatsCount() {
+ return mPackages.size();
+ }
+
+ public PackageStats getPackageStatsAt(int index) {
+ return mPackages.valueAt(index);
+ }
+
+ public PackageStats getPackageStats(String pkgName) {
+ return mPackages.get(pkgName);
+ }
+
+ /** @hide */
+ public PackageStats getOrCreatePackageStats(String pkgName) {
+ PackageStats ps = mPackages.get(pkgName);
+ if (ps == null) {
+ ps = onNewPackageStats(pkgName);
+ mPackages.put(pkgName, ps);
+ }
+ return ps;
+ }
+
+ public int getConfigurationStatsCount() {
+ return mConfigurations.size();
+ }
+
+ public ConfigurationStats getConfigurationStatsAt(int index) {
+ return mConfigurations.valueAt(index);
+ }
+
+ public ConfigurationStats getConfigurationStats(Configuration config) {
+ return mConfigurations.get(config);
+ }
+
+ /** @hide */
+ public ConfigurationStats getOrCreateConfigurationStats(Configuration config) {
+ ConfigurationStats cs = mConfigurations.get(config);
+ if (cs == null) {
+ cs = onNewConfigurationStats(config);
+ mConfigurations.put(config, cs);
+ }
+ return cs;
+ }
+
+ /** @hide */
+ public void clearUsageTimes() {
+ for (int i=mPackages.size()-1; i>=0; i--) {
+ if (mPackages.valueAt(i).clearUsageTimes()) {
+ mPackages.removeAt(i);
+ }
+ }
+ for (int i=mConfigurations.size()-1; i>=0; i--) {
+ if (mConfigurations.valueAt(i).clearUsageTimes()) {
+ mConfigurations.removeAt(i);
+ }
+ }
+ }
+
+ /** @hide */
+ public PackageStats onNewPackageStats(String pkgName) {
+ return new PackageStats(pkgName);
+ }
+
+ /** @hide */
+ public PackageStats onNewPackageStats(Parcel source) {
+ return new PackageStats(source);
+ }
+
+ /** @hide */
+ public ConfigurationStats onNewConfigurationStats(Configuration config) {
+ return new ConfigurationStats(config);
+ }
+
+ /** @hide */
+ public ConfigurationStats onNewConfigurationStats(Parcel source) {
+ return new ConfigurationStats(source);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ writeToParcelInner(dest, parcelableFlags, false);
+ }
+
+ /** @hide */
+ public void writeExtendedToParcel(Parcel dest, int parcelableFlags) {
+ writeToParcelInner(dest, parcelableFlags, true);
+ }
+
+ private void writeToParcelInner(Parcel dest, int parcelableFlags, boolean extended) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+
+ int N = mPackages.size();
+ dest.writeInt(N);
+ for (int i=0; i<N; i++) {
+ PackageStats ps = mPackages.valueAt(i);
+ ps.writeToParcel(dest, parcelableFlags, elapsedRealtime);
+ if (extended) {
+ ps.writeExtendedToParcel(dest, parcelableFlags);
+ }
+ }
+ N = mConfigurations.size();
+ dest.writeInt(N);
+ for (int i=0; i<N; i++) {
+ ConfigurationStats cs = mConfigurations.valueAt(i);
+ cs.writeToParcel(dest, parcelableFlags, elapsedRealtime);
+ if (extended) {
+ cs.writeExtendedToParcel(dest, parcelableFlags);
+ }
+ }
+ }
+
+ public static final Parcelable.Creator<UsageStats> CREATOR
+ = new Parcelable.Creator<UsageStats>() {
+ public UsageStats createFromParcel(Parcel in) {
+ return new UsageStats(in, false);
+ }
+
+ public UsageStats[] newArray(int size) {
+ return new UsageStats[size];
+ }
+ };
+}
diff --git a/core/java/android/app/UsageStatsManager.java b/core/java/android/app/UsageStatsManager.java
new file mode 100644
index 0000000..fbf9c3b
--- /dev/null
+++ b/core/java/android/app/UsageStatsManager.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.Context;
+import android.os.ParcelableParcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import com.android.internal.app.IUsageStats;
+
+/**
+ * Access to usage stats data.
+ * @hide
+ */
+public class UsageStatsManager {
+ final Context mContext;
+ final IUsageStats mService;
+
+ /** @hide */
+ public UsageStatsManager(Context context) {
+ mContext = context;
+ mService = IUsageStats.Stub.asInterface(ServiceManager.getService(
+ Context.USAGE_STATS_SERVICE));
+ }
+
+ public UsageStats getCurrentStats() {
+ try {
+ ParcelableParcel in = mService.getCurrentStats(mContext.getOpPackageName());
+ if (in != null) {
+ return new UsageStats(in.getParcel(), false);
+ }
+ } catch (RemoteException e) {
+ // About to die.
+ }
+ return new UsageStats();
+ }
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 61ff60a..58049fd 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,8 +16,6 @@
package android.app.admin;
-import org.xmlpull.v1.XmlPullParserException;
-
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.content.ComponentName;
@@ -39,6 +37,8 @@ import android.util.Log;
import com.android.org.conscrypt.TrustedCertificateStore;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
@@ -359,8 +359,8 @@ public class DevicePolicyManager {
}
/**
- * Retrieve the current minimum password quality for all admins
- * or a particular one.
+ * Retrieve the current minimum password quality for all admins of this user
+ * and its profiles or a particular one.
* @param admin The name of the admin component to check, or null to aggregate
* all admins.
*/
@@ -412,8 +412,8 @@ public class DevicePolicyManager {
}
/**
- * Retrieve the current minimum password length for all admins
- * or a particular one.
+ * Retrieve the current minimum password length for all admins of this
+ * user and its profiles or a particular one.
* @param admin The name of the admin component to check, or null to aggregate
* all admins.
*/
@@ -467,8 +467,9 @@ public class DevicePolicyManager {
/**
* Retrieve the current number of upper case letters required in the
- * password for all admins or a particular one. This is the same value as
- * set by {#link {@link #setPasswordMinimumUpperCase(ComponentName, int)}
+ * password for all admins of this user and its profiles or a particular one.
+ * This is the same value as set by
+ * {#link {@link #setPasswordMinimumUpperCase(ComponentName, int)}
* and only applies when the password quality is
* {@link #PASSWORD_QUALITY_COMPLEX}.
*
@@ -527,8 +528,9 @@ public class DevicePolicyManager {
/**
* Retrieve the current number of lower case letters required in the
- * password for all admins or a particular one. This is the same value as
- * set by {#link {@link #setPasswordMinimumLowerCase(ComponentName, int)}
+ * password for all admins of this user and its profiles or a particular one.
+ * This is the same value as set by
+ * {#link {@link #setPasswordMinimumLowerCase(ComponentName, int)}
* and only applies when the password quality is
* {@link #PASSWORD_QUALITY_COMPLEX}.
*
@@ -644,8 +646,9 @@ public class DevicePolicyManager {
/**
* Retrieve the current number of numerical digits required in the password
- * for all admins or a particular one. This is the same value as
- * set by {#link {@link #setPasswordMinimumNumeric(ComponentName, int)}
+ * for all admins of this user and its profiles or a particular one.
+ * This is the same value as set by
+ * {#link {@link #setPasswordMinimumNumeric(ComponentName, int)}
* and only applies when the password quality is
* {@link #PASSWORD_QUALITY_COMPLEX}.
*
@@ -760,8 +763,9 @@ public class DevicePolicyManager {
/**
* Retrieve the current number of non-letter characters required in the
- * password for all admins or a particular one. This is the same value as
- * set by {#link {@link #setPasswordMinimumNonLetter(ComponentName, int)}
+ * password for all admins of this user and its profiles or a particular one.
+ * This is the same value as set by
+ * {#link {@link #setPasswordMinimumNonLetter(ComponentName, int)}
* and only applies when the password quality is
* {@link #PASSWORD_QUALITY_COMPLEX}.
*
@@ -868,9 +872,10 @@ public class DevicePolicyManager {
/**
* Get the current password expiration time for the given admin or an aggregate of
- * all admins if admin is null. If the password is expired, this will return the time since
- * the password expired as a negative number. If admin is null, then a composite of all
- * expiration timeouts is returned - which will be the minimum of all timeouts.
+ * all admins of this user and its profiles if admin is null. If the password is
+ * expired, this will return the time since the password expired as a negative number.
+ * If admin is null, then a composite of all expiration timeouts is returned
+ * - which will be the minimum of all timeouts.
*
* @param admin The name of the admin component to check, or null to aggregate all admins.
* @return The password expiration time, in ms.
@@ -887,8 +892,8 @@ public class DevicePolicyManager {
}
/**
- * Retrieve the current password history length for all admins
- * or a particular one.
+ * Retrieve the current password history length for all admins of this
+ * user and its profiles or a particular one.
* @param admin The name of the admin component to check, or null to aggregate
* all admins.
* @return The length of the password history
@@ -923,14 +928,13 @@ public class DevicePolicyManager {
/**
* Determine whether the current password the user has set is sufficient
* to meet the policy requirements (quality, minimum length) that have been
- * requested.
+ * requested by the admins of this user and its profiles.
*
* <p>The calling device admin must have requested
* {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
* this method; if it has not, a security exception will be thrown.
*
- * @return Returns true if the password meets the current requirements,
- * else false.
+ * @return Returns true if the password meets the current requirements, else false.
*/
public boolean isActivePasswordSufficient() {
if (mService != null) {
@@ -993,7 +997,7 @@ public class DevicePolicyManager {
/**
* Retrieve the current maximum number of login attempts that are allowed
- * before the device wipes itself, for all admins
+ * before the device wipes itself, for all admins of this user and its profiles
* or a particular one.
* @param admin The name of the admin component to check, or null to aggregate
* all admins.
@@ -1037,6 +1041,8 @@ public class DevicePolicyManager {
* {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD} to be able to call
* this method; if it has not, a security exception will be thrown.
*
+ * Can not be called from a managed profile.
+ *
* @param password The new password for the user.
* @param flags May be 0 or {@link #RESET_PASSWORD_REQUIRE_ENTRY}.
* @return Returns true if the password was applied, or false if it is
@@ -1077,8 +1083,8 @@ public class DevicePolicyManager {
}
/**
- * Retrieve the current maximum time to unlock for all admins
- * or a particular one.
+ * Retrieve the current maximum time to unlock for all admins of this user
+ * and its profiles or a particular one.
* @param admin The name of the admin component to check, or null to aggregate
* all admins.
*/
@@ -2125,4 +2131,51 @@ public class DevicePolicyManager {
return null;
}
+
+ /**
+ * Sets which components may enter lock task mode.
+ *
+ * This function can only be called by the device owner or the profile owner.
+ * @param components The list of components allowed to enter lock task mode
+ */
+ public void setLockTaskComponents(ComponentName[] components) throws SecurityException {
+ if (mService != null) {
+ try {
+ mService.setLockTaskComponents(components);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * This function returns the list of components allowed to start the lock task mode.
+ * @hide
+ */
+ public ComponentName[] getLockTaskComponents() {
+ if (mService != null) {
+ try {
+ return mService.getLockTaskComponents();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This function lets the caller know whether the given component is allowed to start the
+ * lock task mode.
+ * @param component The component to check
+ */
+ public boolean isLockTaskPermitted(ComponentName component) {
+ if (mService != null) {
+ try {
+ return mService.isLockTaskPermitted(component);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 0096580..03ced0f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -129,4 +129,8 @@ interface IDevicePolicyManager {
void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
String[] getAccountTypesWithManagementDisabled();
+
+ void setLockTaskComponents(in ComponentName[] components);
+ ComponentName[] getLockTaskComponents();
+ boolean isLockTaskPermitted(in ComponentName component);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 042ee28..a059e48 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2683,6 +2683,16 @@ public abstract class Context {
public static final String NETWORK_SCORE_SERVICE = "network_score";
/**
+ * Use with {@link #getSystemService} to retrieve a {@link
+ * android.app.UsageStatsManager} for interacting with the status bar.
+ *
+ * @see #getSystemService
+ * @see android.app.UsageStatsManager
+ * @hide
+ */
+ public static final String USAGE_STATS_SERVICE = "usagestats";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 52a169b..eb2c11f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -936,13 +936,21 @@ public abstract class PackageManager {
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device has at least one camera pointing in
- * some direction.
+ * some direction, or can support an external camera being connected to it.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_CAMERA_ANY = "android.hardware.camera.any";
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device can support having an external camera connected to it.
+ * The external camera may not always be connected or available to applications to use.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CAMERA_EXTERNAL = "android.hardware.camera.external";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device's camera supports flash.
*/
@SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index f161f3a..0ca9161 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -458,7 +458,19 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable {
* 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</p>
+ * 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>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
+ * state. Once the new exposure target is reached, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} will
+ * 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 CaptureRequest#CONTROL_AE_LOCK
+ * @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureResult#CONTROL_AE_STATE
*/
public static final Key<Integer> CONTROL_AE_EXPOSURE_COMPENSATION =
new Key<Integer>("android.control.aeExposureCompensation", int.class);
@@ -469,6 +481,8 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable {
* <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 /
* ON_AUTO_FLASH_REDEYE.</p>
+ * <p>When {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation} is changed, even if the AE lock
+ * is ON, the camera device will still adjust its exposure value.</p>
* <p>If AE precapture is triggered (see {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger})
* when AE is already locked, the camera device will not change the exposure time
* ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}) and sensitivity ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity})
@@ -477,6 +491,7 @@ public final class CaptureRequest extends CameraMetadata implements Parcelable {
* {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.</p>
* <p>See {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE lock related state transition details.</p>
*
+ * @see CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
* @see CaptureResult#CONTROL_AE_STATE
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 51ea447..42a3de8 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -275,7 +275,19 @@ public final class CaptureResult extends CameraMetadata {
* 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</p>
+ * 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>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
+ * state. Once the new exposure target is reached, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} will
+ * 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 CaptureRequest#CONTROL_AE_LOCK
+ * @see CaptureRequest#CONTROL_AE_MODE
+ * @see CaptureResult#CONTROL_AE_STATE
*/
public static final Key<Integer> CONTROL_AE_EXPOSURE_COMPENSATION =
new Key<Integer>("android.control.aeExposureCompensation", int.class);
@@ -286,6 +298,8 @@ public final class CaptureResult extends CameraMetadata {
* <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 /
* ON_AUTO_FLASH_REDEYE.</p>
+ * <p>When {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation} is changed, even if the AE lock
+ * is ON, the camera device will still adjust its exposure value.</p>
* <p>If AE precapture is triggered (see {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger})
* when AE is already locked, the camera device will not change the exposure time
* ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}) and sensitivity ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity})
@@ -294,6 +308,7 @@ public final class CaptureResult extends CameraMetadata {
* {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.</p>
* <p>See {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE lock related state transition details.</p>
*
+ * @see CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION
* @see CaptureRequest#CONTROL_AE_MODE
* @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
* @see CaptureResult#CONTROL_AE_STATE
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index cec90cd..e58c54d 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -156,6 +156,9 @@ public abstract class DisplayManagerInternal {
// If true, enables automatic brightness control.
public boolean useAutoBrightness;
+ //If true, scales the brightness to half of desired.
+ public boolean lowPowerMode;
+
// If true, prevents the screen from completely turning on if it is currently off.
// The display does not enter a "ready" state if this flag is true and screen on is
// blocked. The window manager policy blocks screen on while it prepares the keyguard to
@@ -203,6 +206,7 @@ public abstract class DisplayManagerInternal {
screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment;
useAutoBrightness = other.useAutoBrightness;
blockScreenOn = other.blockScreenOn;
+ lowPowerMode = other.lowPowerMode;
}
@Override
@@ -218,7 +222,8 @@ public abstract class DisplayManagerInternal {
&& screenBrightness == other.screenBrightness
&& screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment
&& useAutoBrightness == other.useAutoBrightness
- && blockScreenOn == other.blockScreenOn;
+ && blockScreenOn == other.blockScreenOn
+ && lowPowerMode == other.lowPowerMode;
}
@Override
@@ -233,7 +238,8 @@ public abstract class DisplayManagerInternal {
+ ", screenBrightness=" + screenBrightness
+ ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment
+ ", useAutoBrightness=" + useAutoBrightness
- + ", blockScreenOn=" + blockScreenOn;
+ + ", blockScreenOn=" + blockScreenOn
+ + ", lowPowerMode=" + lowPowerMode;
}
}
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 8437228..ed223d1 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -36,6 +36,7 @@ import android.view.MotionEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.InputMethodSession;
+import android.view.inputmethod.CursorAnchorInfo;
class IInputMethodSessionWrapper extends IInputMethodSession.Stub
implements HandlerCaller.Callback {
@@ -46,6 +47,7 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
private static final int DO_UPDATE_SELECTION = 90;
private static final int DO_UPDATE_CURSOR = 95;
+ private static final int DO_UPDATE_CURSOR_ANCHOR_INFO = 99;
private static final int DO_APP_PRIVATE_COMMAND = 100;
private static final int DO_TOGGLE_SOFT_INPUT = 105;
private static final int DO_FINISH_SESSION = 110;
@@ -108,6 +110,10 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
mInputMethodSession.updateCursor((Rect)msg.obj);
return;
}
+ case DO_UPDATE_CURSOR_ANCHOR_INFO: {
+ mInputMethodSession.updateCursorAnchorInfo((CursorAnchorInfo)msg.obj);
+ return;
+ }
case DO_APP_PRIVATE_COMMAND: {
SomeArgs args = (SomeArgs)msg.obj;
mInputMethodSession.appPrivateCommand((String)args.arg1,
@@ -181,6 +187,12 @@ class IInputMethodSessionWrapper extends IInputMethodSession.Stub
}
@Override
+ public void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_UPDATE_CURSOR_ANCHOR_INFO, cursorAnchorInfo));
+ }
+
+ @Override
public void appPrivateCommand(String action, Bundle data) {
mCaller.executeOrSendMessage(
mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data));
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f6438b4..4bccaf1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -51,6 +51,7 @@ import android.view.WindowManager;
import android.view.WindowManager.BadTokenException;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
@@ -545,6 +546,17 @@ public class InputMethodService extends AbstractInputMethodService {
public void toggleSoftInput(int showFlags, int hideFlags) {
InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
}
+
+ /**
+ * Call {@link InputMethodService#onUpdateCursorAnchorInfo
+ * InputMethodService.onUpdateCursorAnchorInfo()}.
+ */
+ public void updateCursorAnchorInfo(CursorAnchorInfo info) {
+ if (!isEnabled()) {
+ return;
+ }
+ InputMethodService.this.onUpdateCursorAnchorInfo(info);
+ }
}
/**
@@ -1717,6 +1729,17 @@ public class InputMethodService extends AbstractInputMethodService {
}
/**
+ * Called when the application has reported a new location of its text insertion point and
+ * characters in the composition string. This is only called if explicitly requested by the
+ * input method. The default implementation does nothing.
+ * @param cursorAnchorInfo The positional information of the text insertion point and the
+ * composition string.
+ */
+ public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
+ // Intentionally empty
+ }
+
+ /**
* Update the cursor/anthor monitor mode.
*/
public void setCursorAnchorMonitorMode(int monitorMode) {
diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java
index 804f8ee..862d59e 100644
--- a/core/java/android/net/BaseNetworkStateTracker.java
+++ b/core/java/android/net/BaseNetworkStateTracker.java
@@ -45,6 +45,7 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker {
protected NetworkInfo mNetworkInfo;
protected LinkProperties mLinkProperties;
protected LinkCapabilities mLinkCapabilities;
+ protected Network mNetwork = new Network(ConnectivityManager.INVALID_NET_ID);
private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
@@ -201,4 +202,14 @@ public abstract class BaseNetworkStateTracker implements NetworkStateTracker {
public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
// nothing to do
}
+
+ @Override
+ public void setNetId(int netId) {
+ mNetwork = new Network(netId);
+ }
+
+ @Override
+ public Network getNetwork() {
+ return mNetwork;
+ }
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 30d7043..3e00250 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -408,6 +408,11 @@ public class ConnectivityManager {
*/
public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000;
+ /**
+ * @hide
+ */
+ public final static int INVALID_NET_ID = 0;
+
private final IConnectivityManager mService;
private final String mPackageName;
diff --git a/core/java/com/android/internal/os/PkgUsageStats.aidl b/core/java/android/net/Network.aidl
index 8305271..73ba1af 100644
--- a/core/java/com/android/internal/os/PkgUsageStats.aidl
+++ b/core/java/android/net/Network.aidl
@@ -1,20 +1,20 @@
-/* //device/java/android/android/content/Intent.aidl
+/*
**
-** Copyright 2007, The Android Open Source Project
+** Copyright (C) 2014 The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-package com.android.internal.os;
+package android.net;
-parcelable PkgUsageStats;
+parcelable Network;
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
new file mode 100644
index 0000000..f82bc22
--- /dev/null
+++ b/core/java/android/net/Network.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.Parcelable;
+import android.os.Parcel;
+
+
+/**
+ * Identifies the Network.
+ * @hide
+ */
+public class Network implements Parcelable {
+
+ public final int netId;
+
+ public Network(int netId) {
+ this.netId = netId;
+ }
+
+ public Network(Network that) {
+ this.netId = that.netId;
+ }
+
+ // implement the Parcelable interface
+ public int describeContents() {
+ return 0;
+ }
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(netId);
+ }
+
+ public static final Creator<Network> CREATOR =
+ new Creator<Network>() {
+ public Network createFromParcel(Parcel in) {
+ int netId = in.readInt();
+
+ return new Network(netId);
+ }
+
+ public Network[] newArray(int size) {
+ return new Network[size];
+ }
+ };
+}
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index c49b1d1..29b57a5 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -250,4 +250,14 @@ public interface NetworkStateTracker {
*/
public void stopSampling(SamplingDataTracker.SamplingSnapshot s);
+ /*
+ * Record the current netId
+ */
+ public void setNetId(int netId);
+
+ /*
+ * ?
+ */
+ public Network getNetwork();
+
}
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 8f41e85..daf0065 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -278,7 +278,9 @@ public final class Proxy {
host = p.getHost();
port = Integer.toString(p.getPort());
exclList = p.getExclusionListAsString();
- pacFileUrl = p.getPacFileUrl().toString();
+ if (p.getPacFileUrl() != null) {
+ pacFileUrl = p.getPacFileUrl().toString();
+ }
}
setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
}
diff --git a/core/java/android/nfc/INfcCardEmulation.aidl b/core/java/android/nfc/INfcCardEmulation.aidl
index ae9796b..521f4fd 100644
--- a/core/java/android/nfc/INfcCardEmulation.aidl
+++ b/core/java/android/nfc/INfcCardEmulation.aidl
@@ -34,4 +34,6 @@ interface INfcCardEmulation
AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category);
boolean removeAidGroupForService(int userHandle, in ComponentName service, String category);
List<ApduServiceInfo> getServices(int userHandle, in String category);
+ boolean setPreferredService(in ComponentName service);
+ boolean unsetPreferredService();
}
diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java
index 2820f40..b0449224 100644
--- a/core/java/android/nfc/cardemulation/AidGroup.java
+++ b/core/java/android/nfc/cardemulation/AidGroup.java
@@ -12,10 +12,15 @@ import android.os.Parcelable;
import android.util.Log;
/**
- * The AidGroup class represents a group of ISO/IEC 7816-4
- * Application Identifiers (AIDs) for a specific application
- * category, along with a description resource describing
- * the group.
+ * The AidGroup class represents a group of Application Identifiers (AIDs).
+ *
+ * <p>An instance of this object can be used with
+ * {@link CardEmulation#registerAidGroupForService(android.content.ComponentName, AidGroup)}
+ * to tell the OS which AIDs are handled by your HCE- or SE-based service.
+ *
+ * <p>The format of AIDs is defined in the ISO/IEC 7816-4 specification. This class
+ * requires the AIDs to be input as a hexadecimal string, with an even amount of
+ * hexadecimal characters, e.g. "F014811481".
*/
public final class AidGroup implements Parcelable {
/**
@@ -33,7 +38,7 @@ public final class AidGroup implements Parcelable {
* Creates a new AidGroup object.
*
* @param aids The list of AIDs present in the group
- * @param category The category of this group
+ * @param category The category of this group, e.g. {@link CardEmulation#CATEGORY_PAYMENT}
*/
public AidGroup(ArrayList<String> aids, String category) {
if (aids == null || aids.size() == 0) {
@@ -42,11 +47,12 @@ public final class AidGroup implements Parcelable {
if (aids.size() > MAX_NUM_AIDS) {
throw new IllegalArgumentException("Too many AIDs in AID group.");
}
- if (!isValidCategory(category)) {
- throw new IllegalArgumentException("Category specified is not valid.");
+ if (isValidCategory(category)) {
+ this.category = category;
+ } else {
+ this.category = CardEmulation.CATEGORY_OTHER;
}
this.aids = aids;
- this.category = category;
this.description = null;
}
@@ -158,7 +164,7 @@ public final class AidGroup implements Parcelable {
}
}
- boolean isValidCategory(String category) {
+ static boolean isValidCategory(String category) {
return CardEmulation.CATEGORY_PAYMENT.equals(category) ||
CardEmulation.CATEGORY_OTHER.equals(category);
}
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 94f35ed..f379ee8 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -290,6 +290,20 @@ public final class ApduServiceInfo implements Parcelable {
return groups;
}
+ /**
+ * Returns the category to which this service has attributed the AID that is passed in,
+ * or null if we don't know this AID.
+ */
+ public String getCategoryForAid(String aid) {
+ ArrayList<AidGroup> groups = getAidGroups();
+ for (AidGroup group : groups) {
+ if (group.aids.contains(aid)) {
+ return group.category;
+ }
+ }
+ return null;
+ }
+
public boolean hasCategory(String category) {
return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category));
}
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index 41f039c..e24a22a 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -18,6 +18,7 @@ package android.nfc.cardemulation;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Activity;
import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
@@ -28,6 +29,7 @@ import android.nfc.NfcAdapter;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
import java.util.HashMap;
@@ -248,6 +250,33 @@ public final class CardEmulation {
}
/**
+ * Returns whether the user has allowed AIDs registered in the
+ * specified category to be handled by a service that is preferred
+ * by the foreground application, instead of by a pre-configured default.
+ *
+ * Foreground applications can set such preferences using the
+ * {@link #setPreferredService(Activity, ComponentName)} method.
+ *
+ * @param category The category, e.g. {@link #CATEGORY_PAYMENT}
+ * @return whether AIDs in the category can be handled by a service
+ * specified by the foreground app.
+ */
+ public boolean categoryAllowsForegroundPreference(String category) {
+ if (CATEGORY_PAYMENT.equals(category)) {
+ boolean preferForeground = false;
+ try {
+ preferForeground = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0;
+ } catch (SettingNotFoundException e) {
+ }
+ return preferForeground;
+ } else {
+ // Allowed for all other categories
+ return true;
+ }
+ }
+
+ /**
* Returns the service selection mode for the passed in category.
* Valid return values are:
* <p>{@link #SELECTION_MODE_PREFER_DEFAULT} the user has requested a default
@@ -269,7 +298,6 @@ public final class CardEmulation {
return SELECTION_MODE_ALWAYS_ASK;
}
} else {
- // All other categories are in "only ask if conflict" mode
return SELECTION_MODE_ASK_IF_CONFLICT;
}
}
@@ -283,7 +311,7 @@ public final class CardEmulation {
* that AID group will be replaced with this one.
*
* <p>Note that you can only register AIDs for a service that
- * is running under the same UID as you are. Typically
+ * is running under the same UID as the caller of this API. Typically
* this means you need to call this from the same
* package as the service itself, though UIDs can also
* be shared between packages using shared UIDs.
@@ -352,7 +380,7 @@ public final class CardEmulation {
* method. It will *not* remove AID groups that were statically registered in
* the manifest. If a dynamically registered AID group is removed using
* this method, and a statically registered AID group for the same category
- * exists in the manifest, that AID group will become active again.
+ * exists in the manifest, the static AID group will become active again.
*
* @param service The component name of the service
* @param category The category of the AID group to be removed, e.g. {@link #CATEGORY_PAYMENT}
@@ -378,6 +406,96 @@ public final class CardEmulation {
}
/**
+ * Allows a foreground application to specify which card emulation service
+ * should be preferred while a specific Activity is in the foreground.
+ *
+ * <p>The specified Activity must currently be in resumed state. A good
+ * paradigm is to call this method in your {@link Activity#onResume}, and to call
+ * {@link #unsetPreferredService(Activity)} in your {@link Activity#onPause}.
+ *
+ * <p>This method call will fail in two specific scenarios:
+ * <ul>
+ * <li> If the service registers one or more AIDs in the {@link #CATEGORY_PAYMENT}
+ * category, but the user has indicated that foreground apps are not allowed
+ * to override the default payment service.
+ * <li> If the service registers one or more AIDs in the {@link #CATEGORY_OTHER}
+ * category that are also handled by the default payment service, and the
+ * user has indicated that foreground apps are not allowed to override the
+ * default payment service.
+ * </ul>
+ *
+ * <p> Use {@link #categoryAllowsForegroundPreference(String)} to determine
+ * whether foreground apps can override the default payment service.
+ *
+ * <p>Note that this preference is not persisted by the OS, and hence must be
+ * called every time the Activity is resumed.
+ *
+ * @param activity The activity which prefers this service to be invoked
+ * @param service The service to be preferred while this activity is in the foreground
+ * @return whether the registration was successful
+ */
+ public boolean setPreferredService(Activity activity, ComponentName service) {
+ // Verify the activity is in the foreground before calling into NfcService
+ if (activity == null || service == null) {
+ throw new NullPointerException("activity or service or category is null");
+ }
+ if (!activity.isResumed()) {
+ throw new IllegalArgumentException("Activity must be resumed.");
+ }
+ try {
+ return sService.setPreferredService(service);
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.setPreferredService(service);
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Unsets the preferred service for the specified Activity.
+ *
+ * <p>Note that the specified Activity must still be in resumed
+ * state at the time of this call. A good place to call this method
+ * is in your {@link Activity#onPause} implementation.
+ *
+ * @param activity The activity which the service was registered for
+ * @return true when successful
+ */
+ public boolean unsetPreferredService(Activity activity) {
+ if (activity == null) {
+ throw new NullPointerException("activity is null");
+ }
+ if (!activity.isResumed()) {
+ throw new IllegalArgumentException("Activity must be resumed.");
+ }
+ try {
+ return sService.unsetPreferredService();
+ } catch (RemoteException e) {
+ // Try one more time
+ recoverService();
+ if (sService == null) {
+ Log.e(TAG, "Failed to recover CardEmulationService.");
+ return false;
+ }
+ try {
+ return sService.unsetPreferredService();
+ } catch (RemoteException ee) {
+ Log.e(TAG, "Failed to reach CardEmulationService.");
+ return false;
+ }
+ }
+ }
+
+ /**
* @hide
*/
public boolean setDefaultServiceForCategory(ComponentName service, String category) {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index e78ce33..4857533 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Formatter;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -30,6 +31,8 @@ import android.telephony.SignalStrength;
import android.text.format.DateFormat;
import android.util.Printer;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.util.TimeUtils;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
@@ -135,7 +138,7 @@ public abstract class BatteryStats implements Parcelable {
/**
* Bump the version on this if the checkin format changes.
*/
- private static final int BATTERY_STATS_CHECKIN_VERSION = 7;
+ private static final int BATTERY_STATS_CHECKIN_VERSION = 8;
private static final long BYTES_PER_KB = 1024;
private static final long BYTES_PER_MB = 1048576; // 1024^2
@@ -537,6 +540,7 @@ public abstract class BatteryStats implements Parcelable {
public static final byte CMD_START = 4;
public static final byte CMD_CURRENT_TIME = 5;
public static final byte CMD_OVERFLOW = 6;
+ public static final byte CMD_RESET = 7;
public byte cmd = CMD_NULL;
@@ -620,6 +624,8 @@ public abstract class BatteryStats implements Parcelable {
public static final int EVENT_SYNC = 0x0004;
// Number of event types.
public static final int EVENT_COUNT = 0x0005;
+ // Mask to extract out only the type part of the event.
+ public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
public static final int EVENT_PROC_START = EVENT_PROC | EVENT_FLAG_START;
public static final int EVENT_PROC_FINISH = EVENT_PROC | EVENT_FLAG_FINISH;
@@ -684,7 +690,7 @@ public abstract class BatteryStats implements Parcelable {
dest.writeInt(eventCode);
eventTag.writeToParcel(dest, flags);
}
- if (cmd == CMD_CURRENT_TIME) {
+ if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) {
dest.writeLong(currentTime);
}
}
@@ -722,7 +728,7 @@ public abstract class BatteryStats implements Parcelable {
eventCode = EVENT_NONE;
eventTag = null;
}
- if (cmd == CMD_CURRENT_TIME) {
+ if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) {
currentTime = src.readLong();
} else {
currentTime = 0;
@@ -833,7 +839,59 @@ public abstract class BatteryStats implements Parcelable {
return true;
}
}
-
+
+ public final static class HistoryEventTracker {
+ private final HashMap<String, SparseIntArray>[] mActiveEvents
+ = (HashMap<String, SparseIntArray>[]) new HashMap[HistoryItem.EVENT_COUNT];
+
+ public boolean updateState(int code, String name, int uid, int poolIdx) {
+ if ((code&HistoryItem.EVENT_FLAG_START) != 0) {
+ int idx = code&HistoryItem.EVENT_TYPE_MASK;
+ HashMap<String, SparseIntArray> active = mActiveEvents[idx];
+ if (active == null) {
+ active = new HashMap<String, SparseIntArray>();
+ mActiveEvents[idx] = active;
+ }
+ SparseIntArray uids = active.get(name);
+ if (uids == null) {
+ uids = new SparseIntArray();
+ active.put(name, uids);
+ }
+ if (uids.indexOfKey(uid) >= 0) {
+ // Already set, nothing to do!
+ return false;
+ }
+ uids.put(uid, poolIdx);
+ } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) {
+ int idx = code&HistoryItem.EVENT_TYPE_MASK;
+ HashMap<String, SparseIntArray> active = mActiveEvents[idx];
+ if (active == null) {
+ // not currently active, nothing to do.
+ return false;
+ }
+ SparseIntArray uids = active.get(name);
+ if (uids == null) {
+ // not currently active, nothing to do.
+ return false;
+ }
+ idx = uids.indexOfKey(uid);
+ if (idx < 0) {
+ // not currently active, nothing to do.
+ return false;
+ }
+ uids.removeAt(idx);
+ if (uids.size() <= 0) {
+ active.remove(name);
+ }
+ }
+ return true;
+ }
+
+ public HashMap<String, SparseIntArray> getStateForEvent(int code) {
+ return mActiveEvents[code];
+ }
+ }
+
public static final class BitDescription {
public final int mask;
public final int shift;
@@ -861,7 +919,7 @@ public abstract class BatteryStats implements Parcelable {
this.shortValues = shortValues;
}
}
-
+
public abstract int getHistoryTotalSize();
public abstract int getHistoryUsedSize();
@@ -2958,10 +3016,14 @@ public abstract class BatteryStats implements Parcelable {
pw.print(":");
}
pw.println("START");
- } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
+ } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
+ || rec.cmd == HistoryItem.CMD_RESET) {
if (checkin) {
pw.print(":");
}
+ if (rec.cmd == HistoryItem.CMD_RESET) {
+ pw.print("RESET:");
+ }
pw.print("TIME:");
if (checkin) {
pw.println(rec.currentTime);
@@ -3187,6 +3249,86 @@ public abstract class BatteryStats implements Parcelable {
public static final int DUMP_INCLUDE_HISTORY = 1<<3;
public static final int DUMP_VERBOSE = 1<<4;
+ private void dumpHistoryLocked(PrintWriter pw, int flags, long histStart, boolean checkin) {
+ final HistoryPrinter hprinter = new HistoryPrinter();
+ final HistoryItem rec = new HistoryItem();
+ long lastTime = -1;
+ long baseTime = -1;
+ boolean printed = false;
+ HistoryEventTracker tracker = null;
+ while (getNextHistoryLocked(rec)) {
+ lastTime = rec.time;
+ if (baseTime < 0) {
+ baseTime = lastTime;
+ }
+ if (rec.time >= histStart) {
+ if (histStart >= 0 && !printed) {
+ if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
+ || rec.cmd == HistoryItem.CMD_RESET) {
+ printed = true;
+ } else if (rec.currentTime != 0) {
+ printed = true;
+ byte cmd = rec.cmd;
+ rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+ if (checkin) {
+ pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+ pw.print(HISTORY_DATA); pw.print(',');
+ }
+ hprinter.printNextItem(pw, rec, baseTime, checkin,
+ (flags&DUMP_VERBOSE) != 0);
+ rec.cmd = cmd;
+ }
+ if (tracker != null) {
+ int oldCode = rec.eventCode;
+ HistoryTag oldTag = rec.eventTag;
+ rec.eventTag = new HistoryTag();
+ for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
+ HashMap<String, SparseIntArray> active
+ = tracker.getStateForEvent(i);
+ if (active == null) {
+ continue;
+ }
+ for (HashMap.Entry<String, SparseIntArray> ent
+ : active.entrySet()) {
+ SparseIntArray uids = ent.getValue();
+ for (int j=0; j<uids.size(); j++) {
+ rec.eventCode = i;
+ rec.eventTag.string = ent.getKey();
+ rec.eventTag.uid = uids.keyAt(j);
+ rec.eventTag.poolIdx = uids.valueAt(j);
+ if (checkin) {
+ pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+ pw.print(HISTORY_DATA); pw.print(',');
+ }
+ hprinter.printNextItem(pw, rec, baseTime, checkin,
+ (flags&DUMP_VERBOSE) != 0);
+ }
+ }
+ }
+ rec.eventCode = oldCode;
+ rec.eventTag = oldTag;
+ tracker = null;
+ }
+ }
+ if (checkin) {
+ pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+ pw.print(HISTORY_DATA); pw.print(',');
+ }
+ hprinter.printNextItem(pw, rec, baseTime, checkin,
+ (flags&DUMP_VERBOSE) != 0);
+ } else if (rec.eventCode != HistoryItem.EVENT_NONE) {
+ if (tracker == null) {
+ tracker = new HistoryEventTracker();
+ }
+ tracker.updateState(rec.eventCode, rec.eventTag.string,
+ rec.eventTag.uid, rec.eventTag.poolIdx);
+ }
+ }
+ if (histStart >= 0) {
+ pw.print(checkin ? "NEXT: " : " NEXT: "); pw.println(lastTime+1);
+ }
+ }
+
/**
* Dumps a human-readable summary of the battery statistics to the given PrintWriter.
*
@@ -3200,9 +3342,6 @@ public abstract class BatteryStats implements Parcelable {
(flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) {
- long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
-
- final HistoryItem rec = new HistoryItem();
final long historyTotalSize = getHistoryTotalSize();
final long historyUsedSize = getHistoryUsedSize();
if (startIteratingHistoryLocked()) {
@@ -3218,35 +3357,7 @@ public abstract class BatteryStats implements Parcelable {
pw.print(" strings using ");
printSizeValue(pw, getHistoryStringPoolBytes());
pw.println("):");
- HistoryPrinter hprinter = new HistoryPrinter();
- long lastTime = -1;
- long baseTime = -1;
- boolean printed = false;
- while (getNextHistoryLocked(rec)) {
- lastTime = rec.time;
- if (baseTime < 0) {
- baseTime = lastTime;
- }
- if (rec.time >= histStart) {
- if (histStart >= 0 && !printed) {
- if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
- printed = true;
- } else if (rec.currentTime != 0) {
- printed = true;
- byte cmd = rec.cmd;
- rec.cmd = HistoryItem.CMD_CURRENT_TIME;
- hprinter.printNextItem(pw, rec, baseTime, false,
- (flags&DUMP_VERBOSE) != 0);
- rec.cmd = cmd;
- }
- }
- hprinter.printNextItem(pw, rec, baseTime, false,
- (flags&DUMP_VERBOSE) != 0);
- }
- }
- if (histStart >= 0) {
- pw.print(" NEXT: "); pw.println(lastTime+1);
- }
+ dumpHistoryLocked(pw, flags, histStart, false);
pw.println();
} finally {
finishIteratingHistoryLocked();
@@ -3255,6 +3366,7 @@ public abstract class BatteryStats implements Parcelable {
if (startIteratingOldHistoryLocked()) {
try {
+ final HistoryItem rec = new HistoryItem();
pw.println("Old battery History:");
HistoryPrinter hprinter = new HistoryPrinter();
long baseTime = -1;
@@ -3348,7 +3460,6 @@ public abstract class BatteryStats implements Parcelable {
(flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
if ((flags&DUMP_INCLUDE_HISTORY) != 0 || (flags&DUMP_HISTORY_ONLY) != 0) {
- final HistoryItem rec = new HistoryItem();
if (startIteratingHistoryLocked()) {
try {
for (int i=0; i<getHistoryStringPoolSize(); i++) {
@@ -3365,37 +3476,7 @@ public abstract class BatteryStats implements Parcelable {
pw.print("\"");
pw.println();
}
- HistoryPrinter hprinter = new HistoryPrinter();
- long lastTime = -1;
- long baseTime = -1;
- boolean printed = false;
- while (getNextHistoryLocked(rec)) {
- lastTime = rec.time;
- if (baseTime < 0) {
- baseTime = lastTime;
- }
- if (rec.time >= histStart) {
- if (histStart >= 0 && !printed) {
- if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
- printed = true;
- } else if (rec.currentTime != 0) {
- printed = true;
- byte cmd = rec.cmd;
- rec.cmd = HistoryItem.CMD_CURRENT_TIME;
- pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
- pw.print(HISTORY_DATA); pw.print(',');
- hprinter.printNextItem(pw, rec, baseTime, true, false);
- rec.cmd = cmd;
- }
- }
- pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
- pw.print(HISTORY_DATA); pw.print(',');
- hprinter.printNextItem(pw, rec, baseTime, true, false);
- }
- }
- if (histStart >= 0) {
- pw.print("NEXT: "); pw.println(lastTime+1);
- }
+ dumpHistoryLocked(pw, flags, histStart, true);
} finally {
finishIteratingHistoryLocked();
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index f5ff185..9e03f95 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -455,4 +455,14 @@ interface INetworkManagementService
* Check whether the mobile radio is currently active.
*/
boolean isNetworkActive();
+
+ /**
+ * setup a new network
+ */
+ void createNetwork(int netId, String iface);
+
+ /**
+ * remove a network
+ */
+ void removeNetwork(int netId);
}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 899a958..cd47099 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -36,6 +36,7 @@ interface IUserManager {
Bitmap getUserIcon(int userHandle);
List<UserInfo> getUsers(boolean excludeDying);
List<UserInfo> getProfiles(int userHandle, boolean enabledOnly);
+ UserInfo getProfileParent(int userHandle);
UserInfo getUserInfo(int userHandle);
boolean isRestricted();
void setGuestEnabled(boolean enable);
diff --git a/core/java/android/os/ParcelableParcel.aidl b/core/java/android/os/ParcelableParcel.aidl
new file mode 100644
index 0000000..61f730c
--- /dev/null
+++ b/core/java/android/os/ParcelableParcel.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.os;
+
+parcelable ParcelableParcel;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index e379621..312cdbe 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -301,7 +301,8 @@ public class UserManager {
}
/**
- * Returns the user handle for the user that this application is running for.
+ * Returns the user handle for the user that the calling process is running on.
+ *
* @return the user handle of the user making this call.
* @hide
*/
@@ -617,7 +618,8 @@ public class UserManager {
}
/**
- * Returns a list of UserHandles for profiles associated with this user, including this user.
+ * Returns a list of UserHandles for profiles associated with the user that the calling process
+ * is running on, including the user itself.
*
* @return A non-empty list of UserHandles associated with the calling user.
*/
@@ -638,6 +640,21 @@ public class UserManager {
}
/**
+ * Returns the parent of the profile which this method is called from
+ * or null if called from a user that is not a profile.
+ *
+ * @hide
+ */
+ public UserInfo getProfileParent(int userHandle) {
+ try {
+ return mService.getProfileParent(userHandle);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Could not get profile parent", re);
+ return null;
+ }
+ }
+
+ /**
* If the target user is a managed profile of the calling user or the caller
* is itself a managed profile, then this returns a badged copy of the given
* icon to be able to distinguish it from the original icon.
@@ -664,7 +681,7 @@ public class UserManager {
private int getBadgeResIdForUser(int userHandle) {
// Return the framework-provided badge.
- List<UserInfo> userProfiles = getProfiles(UserHandle.myUserId());
+ List<UserInfo> userProfiles = getProfiles(getUserHandle());
for (UserInfo user : userProfiles) {
if (user.id == userHandle
&& user.isManagedProfile()) {
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index e4f73cb..811751d 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -167,7 +167,7 @@ public final class PrintManager {
/**
* Callback notifying that a print job state changed.
- *
+ *
* @param printJobId The print job id.
*/
public void onPrintJobStateChanged(PrintJobId printJobId);
@@ -175,7 +175,7 @@ public final class PrintManager {
/**
* Creates a new instance.
- *
+ *
* @param context The current context in which to operate.
* @param service The backing system service.
* @hide
@@ -207,13 +207,17 @@ public final class PrintManager {
/**
* Creates an instance that can access all print jobs.
- *
+ *
* @param userId The user id for which to get all print jobs.
* @return An instance if the caller has the permission to access all print
* jobs, null otherwise.
* @hide
*/
public PrintManager getGlobalPrintManagerForUser(int userId) {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return null;
+ }
return new PrintManager(mContext, mService, userId, APP_ID_ANY);
}
@@ -228,11 +232,15 @@ public final class PrintManager {
/**
* Adds a listener for observing the state of print jobs.
- *
+ *
* @param listener The listener to add.
* @hide
*/
public void addPrintJobStateChangeListener(PrintJobStateChangeListener listener) {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return;
+ }
if (mPrintJobStateChangeListeners == null) {
mPrintJobStateChangeListeners = new ArrayMap<PrintJobStateChangeListener,
PrintJobStateChangeListenerWrapper>();
@@ -249,11 +257,15 @@ public final class PrintManager {
/**
* Removes a listener for observing the state of print jobs.
- *
+ *
* @param listener The listener to remove.
* @hide
*/
public void removePrintJobStateChangeListener(PrintJobStateChangeListener listener) {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return;
+ }
if (mPrintJobStateChangeListeners == null) {
return;
}
@@ -275,12 +287,16 @@ public final class PrintManager {
/**
* Gets a print job given its id.
- *
+ *
* @return The print job list.
* @see PrintJob
* @hide
*/
public PrintJob getPrintJob(PrintJobId printJobId) {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return null;
+ }
try {
PrintJobInfo printJob = mService.getPrintJobInfo(printJobId, mAppId, mUserId);
if (printJob != null) {
@@ -294,11 +310,15 @@ public final class PrintManager {
/**
* Gets the print jobs for this application.
- *
+ *
* @return The print job list.
* @see PrintJob
*/
public List<PrintJob> getPrintJobs() {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return Collections.emptyList();
+ }
try {
List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId);
if (printJobInfos == null) {
@@ -317,6 +337,10 @@ public final class PrintManager {
}
void cancelPrintJob(PrintJobId printJobId) {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return;
+ }
try {
mService.cancelPrintJob(printJobId, mAppId, mUserId);
} catch (RemoteException re) {
@@ -325,6 +349,10 @@ public final class PrintManager {
}
void restartPrintJob(PrintJobId printJobId) {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return;
+ }
try {
mService.restartPrintJob(printJobId, mAppId, mUserId);
} catch (RemoteException re) {
@@ -383,6 +411,10 @@ public final class PrintManager {
*/
public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter,
PrintAttributes attributes) {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return null;
+ }
if (!(mContext instanceof Activity)) {
throw new IllegalStateException("Can print only from an activity");
}
@@ -418,11 +450,15 @@ public final class PrintManager {
/**
* Gets the list of enabled print services.
- *
+ *
* @return The enabled service list or an empty list.
* @hide
*/
public List<PrintServiceInfo> getEnabledPrintServices() {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return Collections.emptyList();
+ }
try {
List<PrintServiceInfo> enabledServices = mService.getEnabledPrintServices(mUserId);
if (enabledServices != null) {
@@ -436,11 +472,15 @@ public final class PrintManager {
/**
* Gets the list of installed print services.
- *
+ *
* @return The installed service list or an empty list.
* @hide
*/
public List<PrintServiceInfo> getInstalledPrintServices() {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return Collections.emptyList();
+ }
try {
List<PrintServiceInfo> installedServices = mService.getInstalledPrintServices(mUserId);
if (installedServices != null) {
@@ -456,6 +496,10 @@ public final class PrintManager {
* @hide
*/
public PrinterDiscoverySession createPrinterDiscoverySession() {
+ if (mService == null) {
+ Log.w(LOG_TAG, "Feature android.software.print not available");
+ return null;
+ }
return new PrinterDiscoverySession(mService, mContext, mUserId);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dc618c8..1847b55 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4529,6 +4529,12 @@ public final class Settings {
public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
/**
+ * Whether NFC payment is handled by the foreground application or a default.
+ * @hide
+ */
+ public static final String NFC_PAYMENT_FOREGROUND = "nfc_payment_foreground";
+
+ /**
* Specifies the package name currently configured to be the primary sms application
* @hide
*/
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index d4b29d8..d4919eb 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -17,11 +17,15 @@
package android.service.notification;
import android.service.notification.StatusBarNotification;
+import android.service.notification.NotificationOrderUpdate;
/** @hide */
oneway interface INotificationListener
{
- void onListenerConnected(in String[] notificationKeys);
- void onNotificationPosted(in StatusBarNotification notification);
- void onNotificationRemoved(in StatusBarNotification notification);
+ void onListenerConnected(in NotificationOrderUpdate update);
+ void onNotificationPosted(in StatusBarNotification notification,
+ in NotificationOrderUpdate update);
+ void onNotificationRemoved(in StatusBarNotification notification,
+ in NotificationOrderUpdate update);
+ void onNotificationOrderUpdate(in NotificationOrderUpdate update);
} \ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 3673f03..a94f45a 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -22,10 +22,13 @@ import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.UserHandle;
import android.util.Log;
+import java.util.Comparator;
+import java.util.HashMap;
+
/**
* A service that receives calls from the system when new notifications are posted or removed.
* <p>To extend this class, you must declare the service in your manifest file with
@@ -46,6 +49,7 @@ public abstract class NotificationListenerService extends Service {
+ "[" + getClass().getSimpleName() + "]";
private INotificationListenerWrapper mWrapper = null;
+ private String[] mNotificationKeys;
private INotificationManager mNoMan;
@@ -95,6 +99,15 @@ public abstract class NotificationListenerService extends Service {
// optional
}
+ /**
+ * Implement this method to be notified when the notification order cahnges.
+ *
+ * Call {@link #getOrderedNotificationKeys()} to retrieve the new order.
+ */
+ public void onNotificationOrderUpdate() {
+ // optional
+ }
+
private final INotificationManager getNotificationInterface() {
if (mNoMan == null) {
mNoMan = INotificationManager.Stub.asInterface(
@@ -202,7 +215,7 @@ public abstract class NotificationListenerService extends Service {
* Request the list of outstanding notifications (that is, those that are visible to the
* current user). Useful when you don't know what's already been posted.
*
- * @return An array of active notifications.
+ * @return An array of active notifications, sorted in natural order.
*/
public StatusBarNotification[] getActiveNotifications() {
return getActiveNotifications(null /*all*/);
@@ -213,7 +226,8 @@ public abstract class NotificationListenerService extends Service {
* current user). Useful when you don't know what's already been posted.
*
* @param keys A specific list of notification keys, or {@code null} for all.
- * @return An array of active notifications.
+ * @return An array of active notifications, sorted in natural order
+ * if {@code keys} is {@code null}.
*/
public StatusBarNotification[] getActiveNotifications(String[] keys) {
if (!isBound()) return null;
@@ -226,21 +240,15 @@ public abstract class NotificationListenerService extends Service {
}
/**
- * Request the list of outstanding notification keys(that is, those that are visible to the
- * current user). You can use the notification keys for subsequent retrieval via
+ * Request the list of notification keys in their current natural order.
+ * You can use the notification keys for subsequent retrieval via
* {@link #getActiveNotifications(String[]) or dismissal via
* {@link #cancelNotifications(String[]).
*
- * @return An array of active notification keys.
+ * @return An array of active notification keys, in their natural order.
*/
- public String[] getActiveNotificationKeys() {
- if (!isBound()) return null;
- try {
- return getNotificationInterface().getActiveNotificationKeysFromListener(mWrapper);
- } catch (android.os.RemoteException ex) {
- Log.v(TAG, "Unable to contact notification manager", ex);
- }
- return null;
+ public String[] getOrderedNotificationKeys() {
+ return mNotificationKeys;
}
@Override
@@ -261,28 +269,60 @@ public abstract class NotificationListenerService extends Service {
private class INotificationListenerWrapper extends INotificationListener.Stub {
@Override
- public void onNotificationPosted(StatusBarNotification sbn) {
+ public void onNotificationPosted(StatusBarNotification sbn,
+ NotificationOrderUpdate update) {
try {
- NotificationListenerService.this.onNotificationPosted(sbn);
+ // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+ synchronized (mWrapper) {
+ updateNotificationKeys(update);
+ NotificationListenerService.this.onNotificationPosted(sbn);
+ }
} catch (Throwable t) {
- Log.w(TAG, "Error running onNotificationPosted", t);
+ Log.w(TAG, "Error running onOrderedNotificationPosted", t);
}
}
@Override
- public void onNotificationRemoved(StatusBarNotification sbn) {
+ public void onNotificationRemoved(StatusBarNotification sbn,
+ NotificationOrderUpdate update) {
try {
- NotificationListenerService.this.onNotificationRemoved(sbn);
+ // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+ synchronized (mWrapper) {
+ updateNotificationKeys(update);
+ NotificationListenerService.this.onNotificationRemoved(sbn);
+ }
} catch (Throwable t) {
Log.w(TAG, "Error running onNotificationRemoved", t);
}
}
@Override
- public void onListenerConnected(String[] notificationKeys) {
+ public void onListenerConnected(NotificationOrderUpdate update) {
try {
- NotificationListenerService.this.onListenerConnected(notificationKeys);
+ // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+ synchronized (mWrapper) {
+ updateNotificationKeys(update);
+ NotificationListenerService.this.onListenerConnected(mNotificationKeys);
+ }
} catch (Throwable t) {
Log.w(TAG, "Error running onListenerConnected", t);
}
}
+ @Override
+ public void onNotificationOrderUpdate(NotificationOrderUpdate update)
+ throws RemoteException {
+ try {
+ // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+ synchronized (mWrapper) {
+ updateNotificationKeys(update);
+ NotificationListenerService.this.onNotificationOrderUpdate();
+ }
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onNotificationOrderUpdate", t);
+ }
+ }
+ }
+
+ private void updateNotificationKeys(NotificationOrderUpdate update) {
+ // TODO: avoid garbage by comparing the lists
+ mNotificationKeys = update.getOrderedKeys();
}
}
diff --git a/core/java/android/service/notification/NotificationOrderUpdate.aidl b/core/java/android/service/notification/NotificationOrderUpdate.aidl
new file mode 100644
index 0000000..5d50641
--- /dev/null
+++ b/core/java/android/service/notification/NotificationOrderUpdate.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.service.notification;
+
+parcelable NotificationOrderUpdate;
diff --git a/core/java/android/service/notification/NotificationOrderUpdate.java b/core/java/android/service/notification/NotificationOrderUpdate.java
new file mode 100644
index 0000000..20e19a3
--- /dev/null
+++ b/core/java/android/service/notification/NotificationOrderUpdate.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.notification;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class NotificationOrderUpdate implements Parcelable {
+ // TODO replace this with an update instead of the whole array
+ private final String[] mKeys;
+
+ /** @hide */
+ public NotificationOrderUpdate(String[] keys) {
+ this.mKeys = keys;
+ }
+
+ public NotificationOrderUpdate(Parcel in) {
+ this.mKeys = in.readStringArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeStringArray(this.mKeys);
+ }
+
+ public static final Parcelable.Creator<NotificationOrderUpdate> CREATOR
+ = new Parcelable.Creator<NotificationOrderUpdate>() {
+ public NotificationOrderUpdate createFromParcel(Parcel parcel) {
+ return new NotificationOrderUpdate(parcel);
+ }
+
+ public NotificationOrderUpdate[] newArray(int size) {
+ return new NotificationOrderUpdate[size];
+ }
+ };
+
+ /**
+ * @hide
+ * @return ordered list of keys
+ */
+ String[] getOrderedKeys() {
+ return mKeys;
+ }
+}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 05e202b..2d1016a 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1685,10 +1685,6 @@ public class KeyEvent extends InputEvent implements Parcelable {
case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
case KeyEvent.KEYCODE_BRIGHTNESS_UP:
case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- case KeyEvent.KEYCODE_DPAD_LEFT:
return true;
}
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index a675821..be3b6ce 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -21,6 +21,8 @@ import android.graphics.CanvasProperty;
import android.graphics.Paint;
import android.util.SparseIntArray;
+import com.android.internal.util.VirtualRefBasePtr;
+
import java.lang.ref.WeakReference;
/**
@@ -70,28 +72,32 @@ public final class RenderNodeAnimator {
public static final int DELTA_TYPE_DELTA = 1;
private RenderNode mTarget;
- private long mNativePtr;
+ private VirtualRefBasePtr mNativePtr;
public int mapViewPropertyToRenderProperty(int viewProperty) {
return sViewPropertyAnimatorMap.get(viewProperty);
}
public RenderNodeAnimator(int property, int deltaType, float deltaValue) {
- mNativePtr = nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
- property, deltaType, deltaValue);
+ init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
+ property, deltaType, deltaValue));
}
public RenderNodeAnimator(CanvasProperty<Float> property, int deltaType, float deltaValue) {
- mNativePtr = nCreateCanvasPropertyFloatAnimator(
+ init(nCreateCanvasPropertyFloatAnimator(
new WeakReference<RenderNodeAnimator>(this),
- property.getNativeContainer(), deltaType, deltaValue);
+ property.getNativeContainer(), deltaType, deltaValue));
}
public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField,
int deltaType, float deltaValue) {
- mNativePtr = nCreateCanvasPropertyPaintAnimator(
+ init(nCreateCanvasPropertyPaintAnimator(
new WeakReference<RenderNodeAnimator>(this),
- property.getNativeContainer(), paintField, deltaType, deltaValue);
+ property.getNativeContainer(), paintField, deltaType, deltaValue));
+ }
+
+ private void init(long ptr) {
+ mNativePtr = new VirtualRefBasePtr(ptr);
}
public void start(View target) {
@@ -115,11 +121,11 @@ public final class RenderNodeAnimator {
}
public void setDuration(int duration) {
- nSetDuration(mNativePtr, duration);
+ nSetDuration(mNativePtr.get(), duration);
}
long getNativeAnimator() {
- return mNativePtr;
+ return mNativePtr.get();
}
private void onFinished() {
@@ -134,16 +140,6 @@ public final class RenderNodeAnimator {
}
}
- @Override
- protected void finalize() throws Throwable {
- try {
- nUnref(mNativePtr);
- mNativePtr = 0;
- } finally {
- super.finalize();
- }
- }
-
private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis,
int property, int deltaValueType, float deltaValue);
private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis,
@@ -151,5 +147,4 @@ public final class RenderNodeAnimator {
private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis,
long canvasProperty, int paintField, int deltaValueType, float deltaValue);
private static native void nSetDuration(long nativePtr, int duration);
- private static native void nUnref(long nativePtr);
}
diff --git a/core/java/android/view/inputmethod/CorrectionInfo.java b/core/java/android/view/inputmethod/CorrectionInfo.java
index 1b04e49..a43dfe8 100644
--- a/core/java/android/view/inputmethod/CorrectionInfo.java
+++ b/core/java/android/view/inputmethod/CorrectionInfo.java
@@ -88,16 +88,15 @@ public final class CorrectionInfo implements Parcelable {
/**
* Used to make this class parcelable.
*/
- public static final Parcelable.Creator<CorrectionInfo> CREATOR
- = new Parcelable.Creator<CorrectionInfo>() {
- public CorrectionInfo createFromParcel(Parcel source) {
- return new CorrectionInfo(source);
- }
-
- public CorrectionInfo[] newArray(int size) {
- return new CorrectionInfo[size];
- }
- };
+ public static final Parcelable.Creator<CorrectionInfo> CREATOR =
+ new Parcelable.Creator<CorrectionInfo>() {
+ public CorrectionInfo createFromParcel(Parcel source) {
+ return new CorrectionInfo(source);
+ }
+ public CorrectionInfo[] newArray(int size) {
+ return new CorrectionInfo[size];
+ }
+ };
public int describeContents() {
return 0;
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.aidl b/core/java/android/view/inputmethod/CursorAnchorInfo.aidl
new file mode 100644
index 0000000..2ee9edb
--- /dev/null
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.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.view.inputmethod;
+
+parcelable CursorAnchorInfo;
diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java
new file mode 100644
index 0000000..92455df
--- /dev/null
+++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES 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.inputmethod;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.Layout;
+import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
+
+import java.util.Objects;
+
+/**
+ * Positional information about the text insertion point and characters in the composition string.
+ *
+ * <p>This class encapsulates locations of the text insertion point and the composition string in
+ * the screen coordinates so that IMEs can render their UI components near where the text is
+ * actually inserted.</p>
+ */
+public final class CursorAnchorInfo implements Parcelable {
+ private final int mSelectionStart;
+ private final int mSelectionEnd;
+ private final int mCandidatesStart;
+ private final int mCandidatesEnd;
+
+ /**
+ * Horizontal position of the insertion marker, in the local coordinates that will be
+ * transformed with the transformation matrix when rendered on the screen. This should be
+ * calculated or compatible with {@link Layout#getPrimaryHorizontal(int)}. This can be
+ * {@code java.lang.Float.NaN} when no value is specified.
+ */
+ private final float mInsertionMarkerHorizontal;
+ /**
+ * Vertical position of the insertion marker, in the local coordinates that will be
+ * transformed with the transformation matrix when rendered on the screen. This should be
+ * calculated or compatible with {@link Layout#getLineTop(int)}. This can be
+ * {@code java.lang.Float.NaN} when no value is specified.
+ */
+ private final float mInsertionMarkerTop;
+ /**
+ * Vertical position of the insertion marker, in the local coordinates that will be
+ * transformed with the transformation matrix when rendered on the screen. This should be
+ * calculated or compatible with {@link Layout#getLineBaseline(int)}. This can be
+ * {@code java.lang.Float.NaN} when no value is specified.
+ */
+ private final float mInsertionMarkerBaseline;
+ /**
+ * Vertical position of the insertion marker, in the local coordinates that will be
+ * transformed with the transformation matrix when rendered on the screen. This should be
+ * calculated or compatible with {@link Layout#getLineBottom(int)}. This can be
+ * {@code java.lang.Float.NaN} when no value is specified.
+ */
+ private final float mInsertionMarkerBottom;
+
+ /**
+ * Container of rectangular position of characters, keyed with character index in a unit of
+ * Java chars, in the local coordinates that will be transformed with the transformation matrix
+ * when rendered on the screen.
+ */
+ private final SparseRectFArray mCharacterRects;
+
+ /**
+ * Transformation matrix that is applied to any positional information of this class to
+ * transform local coordinates into screen coordinates.
+ */
+ private final Matrix mMatrix;
+
+ public CursorAnchorInfo(final Parcel source) {
+ mSelectionStart = source.readInt();
+ mSelectionEnd = source.readInt();
+ mCandidatesStart = source.readInt();
+ mCandidatesEnd = source.readInt();
+ mInsertionMarkerHorizontal = source.readFloat();
+ mInsertionMarkerTop = source.readFloat();
+ mInsertionMarkerBaseline = source.readFloat();
+ mInsertionMarkerBottom = source.readFloat();
+ mCharacterRects = source.readParcelable(SparseRectFArray.class.getClassLoader());
+ mMatrix = new Matrix();
+ mMatrix.setValues(source.createFloatArray());
+ }
+
+ /**
+ * 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.writeInt(mSelectionStart);
+ dest.writeInt(mSelectionEnd);
+ dest.writeInt(mCandidatesStart);
+ dest.writeInt(mCandidatesEnd);
+ dest.writeFloat(mInsertionMarkerHorizontal);
+ dest.writeFloat(mInsertionMarkerTop);
+ dest.writeFloat(mInsertionMarkerBaseline);
+ dest.writeFloat(mInsertionMarkerBottom);
+ dest.writeParcelable(mCharacterRects, flags);
+ final float[] matrixArray = new float[9];
+ mMatrix.getValues(matrixArray);
+ dest.writeFloatArray(matrixArray);
+ }
+
+ @Override
+ public int hashCode(){
+ // TODO: Improve the hash function.
+ final float floatHash = mSelectionStart + mSelectionEnd + mCandidatesStart + mCandidatesEnd
+ + mInsertionMarkerHorizontal + mInsertionMarkerTop + mInsertionMarkerBaseline
+ + mInsertionMarkerBottom;
+ int hash = floatHash > 0 ? (int) floatHash : (int)(-floatHash);
+ if (mCharacterRects != null) {
+ hash += mCharacterRects.hashCode();
+ }
+ hash += mMatrix.hashCode();
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj){
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof CursorAnchorInfo)) {
+ return false;
+ }
+ final CursorAnchorInfo that = (CursorAnchorInfo) obj;
+ if (hashCode() != that.hashCode()) {
+ return false;
+ }
+ if (mSelectionStart != that.mSelectionStart
+ || mSelectionEnd != that.mSelectionEnd
+ || mCandidatesStart != that.mCandidatesStart
+ || mCandidatesEnd != that.mCandidatesEnd) {
+ return false;
+ }
+ if (!Objects.equals(mCharacterRects, that.mCharacterRects)) {
+ return false;
+ }
+ if (!Objects.equals(mMatrix, that.mMatrix)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "SelectionInfo{mSelection=" + mSelectionStart + "," + mSelectionEnd
+ + " mCandiadtes=" + mCandidatesStart + "," + mCandidatesEnd
+ + " mInsertionMarkerHorizontal=" + mInsertionMarkerHorizontal
+ + " mInsertionMarkerTop=" + mInsertionMarkerTop
+ + " mInsertionMarkerBaseline=" + mInsertionMarkerBaseline
+ + " mInsertionMarkerBottom=" + mInsertionMarkerBottom
+ + " mCharacterRects=" + (mCharacterRects != null ? mCharacterRects : "null")
+ + " mMatrix=" + mMatrix
+ + "}";
+ }
+
+ /**
+ * Builder for {@link CursorAnchorInfo}. This class is not designed to be thread-safe.
+ */
+ public static final class CursorAnchorInfoBuilder {
+ /**
+ * 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) {
+ mSelectionStart = newStart;
+ mSelectionEnd = newEnd;
+ return this;
+ }
+ private int mSelectionStart = -1;
+ private int mSelectionEnd = -1;
+
+ /**
+ * Sets the text range of the composition string. Calling this can be skipped if there is
+ * no composition.
+ */
+ public CursorAnchorInfoBuilder setCandidateRange(final int start, final int end) {
+ mCandidateStart = start;
+ mCandidateEnd = end;
+ return this;
+ }
+ private int mCandidateStart = -1;
+ private int mCandidateEnd = -1;
+
+ /**
+ * Sets the location of the text insertion point (zero width cursor) as a rectangle in
+ * local coordinates. Calling this can be skipped when there is no text insertion point;
+ * however if there is an insertion point, editors must call this method.
+ * @param horizontalPosition horizontal position of the insertion marker, in the local
+ * coordinates that will be transformed with the transformation matrix when rendered on the
+ * screen. This should be calculated or compatible with
+ * {@link Layout#getPrimaryHorizontal(int)}.
+ * @param lineTop vertical position of the insertion marker, in the local coordinates that
+ * will be transformed with the transformation matrix when rendered on the screen. This
+ * should be calculated or compatible with {@link Layout#getLineTop(int)}.
+ * @param lineBaseline vertical position of the insertion marker, in the local coordinates
+ * that will be transformed with the transformation matrix when rendered on the screen. This
+ * should be calculated or compatible with {@link Layout#getLineBaseline(int)}.
+ * @param lineBottom vertical position of the insertion marker, in the local coordinates
+ * 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){
+ mInsertionMarkerHorizontal = horizontalPosition;
+ mInsertionMarkerTop = lineTop;
+ mInsertionMarkerBaseline = lineBaseline;
+ mInsertionMarkerBottom = lineBottom;
+ return this;
+ }
+ private float mInsertionMarkerHorizontal = Float.NaN;
+ private float mInsertionMarkerTop = Float.NaN;
+ private float mInsertionMarkerBaseline = Float.NaN;
+ private float mInsertionMarkerBottom = Float.NaN;
+
+ /**
+ * Adds the bounding box of the character specified with the index.
+ * <p>
+ * Editor authors should not call this method for characters that are invisible.
+ * </p>
+ *
+ * @param index index of the character in Java chars units. Must be specified in
+ * ascending order across successive calls.
+ * @param leadingEdgeX x coordinate of the leading edge of the character in local
+ * coordinates, that is, left edge for LTR text and right edge for RTL text.
+ * @param leadingEdgeY y coordinate of the leading edge of the character in local
+ * coordinates.
+ * @param trailingEdgeX x coordinate of the trailing edge of the character in local
+ * coordinates, that is, right edge for LTR text and left edge for RTL text.
+ * @param trailingEdgeY y coordinate of the trailing edge of the character in local
+ * coordinates.
+ * @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) {
+ if (index < 0) {
+ throw new IllegalArgumentException("index must not be a negative integer.");
+ }
+ if (mCharacterRectBuilder == null) {
+ mCharacterRectBuilder = new SparseRectFArrayBuilder();
+ }
+ mCharacterRectBuilder.append(index, leadingEdgeX, leadingEdgeY, trailingEdgeX,
+ trailingEdgeY);
+ return this;
+ }
+ private SparseRectFArrayBuilder mCharacterRectBuilder = null;
+
+ /**
+ * Sets the matrix that transforms local coordinates into screen coordinates.
+ * @param matrix transformation matrix from local coordinates into screen coordinates. null
+ * is interpreted as an identity matrix.
+ */
+ public CursorAnchorInfoBuilder setMatrix(final Matrix matrix) {
+ if (matrix != null) {
+ mMatrix = matrix;
+ } else {
+ mMatrix = Matrix.IDENTITY_MATRIX;
+ }
+ return this;
+ }
+ private Matrix mMatrix = Matrix.IDENTITY_MATRIX;
+
+ /**
+ * @return {@link CursorAnchorInfo} using parameters in this
+ * {@link CursorAnchorInfoBuilder}.
+ */
+ public CursorAnchorInfo build() {
+ return new CursorAnchorInfo(this);
+ }
+
+ /**
+ * Resets the internal state so that this instance can be reused to build another
+ * instance of {@link CursorAnchorInfo}.
+ */
+ public void reset() {
+ mSelectionStart = -1;
+ mSelectionEnd = -1;
+ mCandidateStart = -1;
+ mCandidateEnd = -1;
+ mInsertionMarkerHorizontal = Float.NaN;
+ mInsertionMarkerTop = Float.NaN;
+ mInsertionMarkerBaseline = Float.NaN;
+ mInsertionMarkerBottom = Float.NaN;
+ mMatrix = Matrix.IDENTITY_MATRIX;
+ if (mCharacterRectBuilder != null) {
+ mCharacterRectBuilder.reset();
+ }
+ }
+ }
+
+ private CursorAnchorInfo(final CursorAnchorInfoBuilder builder) {
+ mSelectionStart = builder.mSelectionStart;
+ mSelectionEnd = builder.mSelectionEnd;
+ mCandidatesStart = builder.mCandidateStart;
+ mCandidatesEnd = builder.mCandidateEnd;
+ mInsertionMarkerHorizontal = builder.mInsertionMarkerHorizontal;
+ mInsertionMarkerTop = builder.mInsertionMarkerTop;
+ mInsertionMarkerBaseline = builder.mInsertionMarkerBaseline;
+ mInsertionMarkerBottom = builder.mInsertionMarkerBottom;
+ mCharacterRects = builder.mCharacterRectBuilder != null ?
+ builder.mCharacterRectBuilder.build() : null;
+ mMatrix = builder.mMatrix;
+ }
+
+ /**
+ * Returns the index where the selection starts.
+ * @return -1 if there is no selection.
+ */
+ public int getSelectionStart() {
+ return mSelectionStart;
+ }
+
+ /**
+ * Returns the index where the selection ends.
+ * @return -1 if there is no selection.
+ */
+ public int getSelectionEnd() {
+ return mSelectionEnd;
+ }
+
+ /**
+ * Returns the index where the composition starts.
+ * @return -1 if there is no composition.
+ */
+ public int getCandidatesStart() {
+ return mCandidatesStart;
+ }
+
+ /**
+ * Returns the index where the composition ends.
+ * @return -1 if there is no composition.
+ */
+ public int getCandidatesEnd() {
+ return mCandidatesEnd;
+ }
+
+ /**
+ * Returns the horizontal start of the insertion marker, in the local coordinates that will
+ * be transformed with {@link #getMatrix()} when rendered on the screen.
+ * @return x coordinate that is compatible with {@link Layout#getPrimaryHorizontal(int)}.
+ * Pay special care to RTL/LTR handling.
+ * {@code java.lang.Float.NaN} if not specified.
+ * @see Layout#getPrimaryHorizontal(int)
+ */
+ public float getInsertionMarkerHorizontal() {
+ return mInsertionMarkerHorizontal;
+ }
+ /**
+ * Returns the vertical top position of the insertion marker, in the local coordinates that
+ * will be transformed with {@link #getMatrix()} when rendered on the screen.
+ * @return y coordinate that is compatible with {@link Layout#getLineTop(int)}.
+ * {@code java.lang.Float.NaN} if not specified.
+ */
+ public float getInsertionMarkerTop() {
+ return mInsertionMarkerTop;
+ }
+ /**
+ * Returns the vertical baseline position of the insertion marker, in the local coordinates
+ * that will be transformed with {@link #getMatrix()} when rendered on the screen.
+ * @return y coordinate that is compatible with {@link Layout#getLineBaseline(int)}.
+ * {@code java.lang.Float.NaN} if not specified.
+ */
+ public float getInsertionMarkerBaseline() {
+ return mInsertionMarkerBaseline;
+ }
+ /**
+ * Returns the vertical bottom position of the insertion marker, in the local coordinates
+ * that will be transformed with {@link #getMatrix()} when rendered on the screen.
+ * @return y coordinate that is compatible with {@link Layout#getLineBottom(int)}.
+ * {@code java.lang.Float.NaN} if not specified.
+ */
+ public float getInsertionMarkerBottom() {
+ return mInsertionMarkerBottom;
+ }
+
+ /**
+ * Returns a new instance of {@link RectF} that indicates the location of the character
+ * specified with the index.
+ * <p>
+ * Note that coordinates are not necessarily contiguous or even monotonous, especially when
+ * RTL text and LTR text are mixed.
+ * </p>
+ * @param index index of the character in a Java chars.
+ * @return a new instance of {@link RectF} that represents the location of the character in
+ * local coordinates. null if the character is invisible or the application did not provide
+ * the location. Note that the {@code left} field can be greater than the {@code right} field
+ * if the character is in RTL text.
+ */
+ // TODO: Prepare a document about the expected behavior for surrogate pairs, combining
+ // characters, and non-graphical chars.
+ public RectF getCharacterRect(final int index) {
+ if (mCharacterRects == null) {
+ return null;
+ }
+ return mCharacterRects.get(index);
+ }
+
+ /**
+ * Returns a new instance of {@link android.graphics.Matrix} that indicates the transformation
+ * matrix that is to be applied other positional data in this class.
+ * @return a new instance (copy) of the transformation matrix.
+ */
+ public Matrix getMatrix() {
+ return new Matrix(mMatrix);
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final Parcelable.Creator<CursorAnchorInfo> CREATOR
+ = new Parcelable.Creator<CursorAnchorInfo>() {
+ @Override
+ public CursorAnchorInfo createFromParcel(Parcel source) {
+ return new CursorAnchorInfo(source);
+ }
+
+ @Override
+ public CursorAnchorInfo[] newArray(int size) {
+ return new CursorAnchorInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0227873..be0c27d 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -321,7 +321,6 @@ public final class InputMethodManager {
* The buffer to retrieve the view location in screen coordinates in {@link #updateCursor}.
*/
private final int[] mViewTopLeft = new int[2];
-
// -----------------------------------------------------------
/**
@@ -1435,7 +1434,7 @@ public final class InputMethodManager {
|| mCurrentTextBoxAttribute == null || mCurMethod == null) {
return;
}
-
+
if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
|| mCursorCandStart != candidatesStart
|| mCursorCandEnd != candidatesEnd) {
@@ -1556,6 +1555,31 @@ public final class InputMethodManager {
}
/**
+ * Report positional change of the text insertion point and/or characters in the composition
+ * string.
+ */
+ public void updateCursorAnchorInfo(View view, final CursorAnchorInfo cursorAnchorInfo) {
+ if (view == null || cursorAnchorInfo == null) {
+ return;
+ }
+ checkFocus();
+ synchronized (mH) {
+ if ((mServedView != view &&
+ (mServedView == null || !mServedView.checkInputConnectionProxy(view)))
+ || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "updateCursorAnchorInfo");
+
+ try {
+ mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
+ } catch (RemoteException e) {
+ Log.w(TAG, "IME died: " + mCurId, e);
+ }
+ }
+ }
+
+ /**
* Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
* InputMethodSession.appPrivateCommand()} on the current Input Method.
* @param view Optional View that is sending the command, or null if
diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java
index 6386299..74fbbc7 100644
--- a/core/java/android/view/inputmethod/InputMethodSession.java
+++ b/core/java/android/view/inputmethod/InputMethodSession.java
@@ -165,7 +165,7 @@ public interface InputMethodSession {
public void appPrivateCommand(String action, Bundle data);
/**
- * Toggle the soft input window.
+ * Toggle the soft input window.
* Applications can toggle the state of the soft input window.
* @param showFlags Provides additional operating flags. May be
* 0 or have the {@link InputMethodManager#SHOW_IMPLICIT},
@@ -175,4 +175,14 @@ public interface InputMethodSession {
* {@link InputMethodManager#HIDE_NOT_ALWAYS} bit set.
*/
public void toggleSoftInput(int showFlags, int hideFlags);
+
+ /**
+ * This method is called when the cursor and/or the character position relevant to text input
+ * is changed on the screen. This is not called by default. It will only be reported if
+ * requested by the input method.
+ *
+ * @param cursorAnchorInfo Positional information relevant to text input, such as text
+ * insertion point and composition string.
+ */
+ public void updateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo);
}
diff --git a/core/java/android/view/inputmethod/SparseRectFArray.java b/core/java/android/view/inputmethod/SparseRectFArray.java
new file mode 100644
index 0000000..40cade7
--- /dev/null
+++ b/core/java/android/view/inputmethod/SparseRectFArray.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES 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.inputmethod;
+
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * An implementation of SparseArray specialized for {@link android.graphics.RectF}.
+ * <p>
+ * As this is a sparse array, it represents an array of {@link RectF} most of which are null. This
+ * class could be in some other packages like android.graphics or android.util but currently
+ * belong to android.view.inputmethod because this class is hidden and used only in input method
+ * framework.
+ * </p>
+ * @hide
+ */
+public final class SparseRectFArray implements Parcelable {
+ /**
+ * The keys, in ascending order, of those {@link RectF} that are not null. For example,
+ * {@code [null, null, null, Rect1, null, Rect2]} would be represented by {@code [3,5]}.
+ * @see #mCoordinates
+ */
+ private final int[] mKeys;
+
+ /**
+ * Stores coordinates of the rectangles, in the order of
+ * {@code rects[mKeys[0]].left}, {@code rects[mKeys[0]].top},
+ * {@code rects[mKeys[0]].right}, {@code rects[mKeys[0]].bottom},
+ * {@code rects[mKeys[1]].left}, {@code rects[mKeys[1]].top},
+ * {@code rects[mKeys[1]].right}, {@code rects[mKeys[1]].bottom},
+ * {@code rects[mKeys[2]].left}, {@code rects[mKeys[2]].top}, ....
+ */
+ private final float[] mCoordinates;
+
+ public SparseRectFArray(final Parcel source) {
+ mKeys = source.createIntArray();
+ mCoordinates = source.createFloatArray();
+ }
+
+ /**
+ * 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.writeIntArray(mKeys);
+ dest.writeFloatArray(mCoordinates);
+ }
+
+ @Override
+ public int hashCode() {
+ // TODO: Improve the hash function.
+ if (mKeys == null || mKeys.length == 0) {
+ return 0;
+ }
+ int hash = mKeys.length;
+ // For performance reasons, only the first rectangle is used for the hash code now.
+ for (int i = 0; i < 4; i++) {
+ hash *= 31;
+ hash += mCoordinates[i];
+ }
+ return hash;
+ }
+
+ @Override
+ public boolean equals(Object obj){
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof SparseRectFArray)) {
+ return false;
+ }
+ final SparseRectFArray that = (SparseRectFArray) obj;
+
+ return Arrays.equals(mKeys, that.mKeys) && Arrays.equals(mCoordinates, that.mCoordinates);
+ }
+
+ @Override
+ public String toString() {
+ if (mKeys == null || mCoordinates == null) {
+ return "SparseRectFArray{}";
+ }
+ final StringBuilder sb = new StringBuilder();
+ sb.append("SparseRectFArray{");
+ for (int i = 0; i < mKeys.length; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ final int baseIndex = i * 4;
+ sb.append(mKeys[i]);
+ sb.append(":[");
+ sb.append(mCoordinates[baseIndex + 0]);
+ sb.append(",");
+ sb.append(mCoordinates[baseIndex + 1]);
+ sb.append("],[");
+ sb.append(mCoordinates[baseIndex + 2]);
+ sb.append(",");
+ sb.append(mCoordinates[baseIndex + 3]);
+ sb.append("]");
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ /**
+ * Builder for {@link SparseRectFArray}. This class is not designed to be thread-safe.
+ * @hide
+ */
+ public static final class SparseRectFArrayBuilder {
+ /**
+ * Throws {@link IllegalArgumentException} to make sure that this class is correctly used.
+ * @param key key to be checked.
+ */
+ private void checkIndex(final int key) {
+ if (mCount == 0) {
+ return;
+ }
+ if (mKeys[mCount - 1] >= key) {
+ throw new IllegalArgumentException("key must be greater than all existing keys.");
+ }
+ }
+
+ /**
+ * Extends the internal array if necessary.
+ */
+ private void ensureBufferSize() {
+ if (mKeys == null) {
+ mKeys = new int[INITIAL_SIZE];
+ }
+ if (mCoordinates == null) {
+ mCoordinates = new float[INITIAL_SIZE * 4];
+ }
+ final int requiredIndexArraySize = mCount + 1;
+ if (mKeys.length <= requiredIndexArraySize) {
+ final int[] newArray = new int[requiredIndexArraySize * 2];
+ System.arraycopy(mKeys, 0, newArray, 0, mCount);
+ mKeys = newArray;
+ }
+ final int requiredCoordinatesArraySize = (mCount + 1) * 4;
+ if (mCoordinates.length <= requiredCoordinatesArraySize) {
+ final float[] newArray = new float[requiredCoordinatesArraySize * 2];
+ System.arraycopy(mCoordinates, 0, newArray, 0, mCount * 4);
+ mCoordinates = newArray;
+ }
+ }
+
+ /**
+ * Puts the rectangle with an integer key.
+ * @param key the key to be associated with the rectangle. It must be greater than all
+ * existing keys that have been previously specified.
+ * @param left left of the rectangle.
+ * @param top top of the rectangle.
+ * @param right right of the rectangle.
+ * @param bottom bottom of the rectangle.
+ * @return the receiver object itself for chaining method calls.
+ * @throws IllegalArgumentException If the index is not greater than all of existing keys.
+ */
+ public SparseRectFArrayBuilder append(final int key,
+ final float left, final float top, final float right, final float bottom) {
+ checkIndex(key);
+ ensureBufferSize();
+ final int baseCoordinatesIndex = mCount * 4;
+ mCoordinates[baseCoordinatesIndex + 0] = left;
+ mCoordinates[baseCoordinatesIndex + 1] = top;
+ mCoordinates[baseCoordinatesIndex + 2] = right;
+ mCoordinates[baseCoordinatesIndex + 3] = bottom;
+ mKeys[mCount] = key;
+ ++mCount;
+ return this;
+ }
+ private int mCount = 0;
+ private int[] mKeys = null;
+ private float[] mCoordinates = null;
+ private static int INITIAL_SIZE = 16;
+
+ /**
+ * @return {@link SparseRectFArray} using parameters in this {@link SparseRectFArray}.
+ */
+ public SparseRectFArray build() {
+ return new SparseRectFArray(this);
+ }
+
+ public void reset() {
+ if (mCount == 0) {
+ mKeys = null;
+ mCoordinates = null;
+ }
+ mCount = 0;
+ }
+ }
+
+ private SparseRectFArray(final SparseRectFArrayBuilder builder) {
+ if (builder.mCount == 0) {
+ mKeys = null;
+ mCoordinates = null;
+ } else {
+ mKeys = new int[builder.mCount];
+ mCoordinates = new float[builder.mCount * 4];
+ System.arraycopy(builder.mKeys, 0, mKeys, 0, builder.mCount);
+ System.arraycopy(builder.mCoordinates, 0, mCoordinates, 0, builder.mCount * 4);
+ }
+ }
+
+ public RectF get(final int index) {
+ if (mKeys == null) {
+ return null;
+ }
+ if (index < 0) {
+ return null;
+ }
+ final int arrayIndex = Arrays.binarySearch(mKeys, index);
+ if (arrayIndex < 0) {
+ return null;
+ }
+ final int baseCoordIndex = arrayIndex * 4;
+ return new RectF(mCoordinates[baseCoordIndex],
+ mCoordinates[baseCoordIndex + 1],
+ mCoordinates[baseCoordIndex + 2],
+ mCoordinates[baseCoordIndex + 3]);
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final Parcelable.Creator<SparseRectFArray> CREATOR =
+ new Parcelable.Creator<SparseRectFArray>() {
+ @Override
+ public SparseRectFArray createFromParcel(Parcel source) {
+ return new SparseRectFArray(source);
+ }
+ @Override
+ public SparseRectFArray[] newArray(int size) {
+ return new SparseRectFArray[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
+
diff --git a/core/java/android/webkit/EventLogTags.logtags b/core/java/android/webkit/EventLogTags.logtags
index b0b5493..a90aebd 100644
--- a/core/java/android/webkit/EventLogTags.logtags
+++ b/core/java/android/webkit/EventLogTags.logtags
@@ -8,3 +8,4 @@ option java_package android.webkit;
# 70103- used by the browser app itself
70150 browser_snap_center
+70151 exp_det_attempt_to_call_object_getclass (app_signature|3)
diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java
index 3e33498..fa760b7 100644
--- a/core/java/android/webkit/PermissionRequest.java
+++ b/core/java/android/webkit/PermissionRequest.java
@@ -28,6 +28,7 @@ import android.net.Uri;
public interface PermissionRequest {
/**
* Resource belongs to geolocation service.
+ * @hide - see b/14668406
*/
public final static long RESOURCE_GEOLOCATION = 1 << 0;
/**
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 25bcd44..ac12357 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -50,28 +50,6 @@ public final class WebViewFactory {
private static WebViewFactoryProvider sProviderInstance;
private static final Object sProviderLock = new Object();
- public static boolean isExperimentalWebViewAvailable() {
- // TODO: Remove callers of this method then remove it.
- return false; // Hide the toggle in Developer Settings.
- }
-
- /** @hide */
- public static void setUseExperimentalWebView(boolean enable) {
- // TODO: Remove callers of this method then remove it.
- }
-
- /** @hide */
- public static boolean useExperimentalWebView() {
- // TODO: Remove callers of this method then remove it.
- return true;
- }
-
- /** @hide */
- public static boolean isUseExperimentalWebViewSet() {
- // TODO: Remove callers of this method then remove it.
- return false; // User has not modifed Developer Settings
- }
-
static WebViewFactoryProvider getProvider() {
synchronized (sProviderLock) {
// For now the main purpose of this function (and the factory abstraction) is to keep
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index f4cd5fc..d9a4f57 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -743,7 +743,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
*
* @param view The view whose scroll state is being reported
*
- * @param scrollState The current scroll state. One of
+ * @param scrollState The current scroll state. One of
* {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}.
*/
public void onScrollStateChanged(AbsListView view, int scrollState);
@@ -3267,7 +3267,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
}
- private boolean startScrollIfNeeded(int y, MotionEvent vtev) {
+ private boolean startScrollIfNeeded(int x, int y, MotionEvent vtev) {
// Check if we have moved far enough that it looks more like a
// scroll than a tap
final int deltaY = y - mMotionY;
@@ -3296,27 +3296,31 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
- scrollIfNeeded(y, vtev);
+ scrollIfNeeded(x, y, vtev);
return true;
}
return false;
}
- private void scrollIfNeeded(int y, MotionEvent vtev) {
+ private void scrollIfNeeded(int x, int y, MotionEvent vtev) {
int rawDeltaY = y - mMotionY;
+ int scrollOffsetCorrection = 0;
+ int scrollConsumedCorrection = 0;
+ if (mLastY == Integer.MIN_VALUE) {
+ rawDeltaY -= mMotionCorrection;
+ }
if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) {
rawDeltaY -= mScrollConsumed[1];
- mMotionCorrection -= mScrollOffset[1];
- if (mLastY != Integer.MIN_VALUE) {
- mLastY -= mScrollOffset[1] + mScrollConsumed[1];
- }
+ scrollOffsetCorrection -= mScrollOffset[1];
+ scrollConsumedCorrection -= mScrollConsumed[1];
if (vtev != null) {
vtev.offsetLocation(0, mScrollOffset[1]);
}
}
- final int deltaY = rawDeltaY - mMotionCorrection;
- int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
+ final int deltaY = rawDeltaY;
+ int incrementalDeltaY =
+ mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY;
int lastYCorrection = 0;
if (mTouchMode == TOUCH_MODE_SCROLL) {
@@ -3378,46 +3382,51 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
(motionViewRealTop - motionViewPrevTop);
if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll,
mScrollOffset)) {
- mMotionCorrection -= mScrollOffset[1];
lastYCorrection -= mScrollOffset[1];
if (vtev != null) {
vtev.offsetLocation(0, mScrollOffset[1]);
}
} else {
- overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
- 0, mOverscrollDistance, true);
- if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
- // Don't allow overfling if we're at the edge.
- if (mVelocityTracker != null) {
- mVelocityTracker.clear();
- }
+ final boolean atOverscrollEdge = overScrollBy(0, overscroll,
+ 0, mScrollY, 0, 0, 0, mOverscrollDistance, true);
+
+ if (atOverscrollEdge && mVelocityTracker != null) {
+ // Don't allow overfling if we're at the edge
+ mVelocityTracker.clear();
}
final int overscrollMode = getOverScrollMode();
if (overscrollMode == OVER_SCROLL_ALWAYS ||
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
!contentFits())) {
- mDirection = 0; // Reset when entering overscroll.
- mTouchMode = TOUCH_MODE_OVERSCROLL;
- if (deltaY > 0) {
- mEdgeGlowTop.onPull((float) overscroll / getHeight());
+ if (!atOverscrollEdge) {
+ mDirection = 0; // Reset when entering overscroll.
+ mTouchMode = TOUCH_MODE_OVERSCROLL;
+ }
+ if (incrementalDeltaY > 0) {
+ mEdgeGlowTop.onPull((float) overscroll / getHeight(),
+ (float) x / getWidth());
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
- invalidate(mEdgeGlowTop.getBounds(false));
- } else if (deltaY < 0) {
- mEdgeGlowBottom.onPull((float) overscroll / getHeight());
+ invalidate(0, 0, getWidth(),
+ mEdgeGlowTop.getMaxHeight() + getPaddingTop());
+ } else if (incrementalDeltaY < 0) {
+ mEdgeGlowBottom.onPull((float) overscroll / getHeight(),
+ 1.f - (float) x / getWidth());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
- invalidate(mEdgeGlowBottom.getBounds(true));
+ invalidate(0, getHeight() - getPaddingBottom() -
+ mEdgeGlowBottom.getMaxHeight(), getWidth(),
+ getHeight());
}
}
}
}
- mMotionY = y;
+ mMotionY = y + scrollOffsetCorrection;
}
- mLastY = y + lastYCorrection;
+ mLastY = y + lastYCorrection + scrollOffsetCorrection;
}
} else if (mTouchMode == TOUCH_MODE_OVERSCROLL) {
if (y != mLastY) {
@@ -3445,17 +3454,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
(overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
!contentFits())) {
if (rawDeltaY > 0) {
- mEdgeGlowTop.onPull((float) overScrollDistance / getHeight());
+ mEdgeGlowTop.onPull((float) overScrollDistance / getHeight(),
+ (float) x / getWidth());
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
- invalidate(mEdgeGlowTop.getBounds(false));
+ invalidate(0, 0, getWidth(),
+ mEdgeGlowTop.getMaxHeight() + getPaddingTop());
} else if (rawDeltaY < 0) {
- mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight());
+ mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight(),
+ 1.f - (float) x / getWidth());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
- invalidate(mEdgeGlowBottom.getBounds(true));
+ invalidate(0, getHeight() - getPaddingBottom() -
+ mEdgeGlowBottom.getMaxHeight(), getWidth(),
+ getHeight());
}
}
}
@@ -3703,7 +3717,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
case TOUCH_MODE_DONE_WAITING:
// Check if we have moved far enough that it looks more like a
// scroll than a tap. If so, we'll enter scrolling mode.
- if (startScrollIfNeeded(y, vtev)) {
+ if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, vtev)) {
break;
}
// Otherwise, check containment within list bounds. If we're
@@ -3723,7 +3737,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
break;
case TOUCH_MODE_SCROLL:
case TOUCH_MODE_OVERSCROLL:
- scrollIfNeeded(y, vtev);
+ scrollIfNeeded((int) ev.getX(pointerIndex), y, vtev);
break;
}
}
@@ -4022,8 +4036,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
canvas.translate(leftPadding, edgeY);
mEdgeGlowTop.setSize(width, getHeight());
if (mEdgeGlowTop.draw(canvas)) {
- mEdgeGlowTop.setPosition(leftPadding, edgeY);
- invalidate(mEdgeGlowTop.getBounds(false));
+ invalidate(0, 0, getWidth(),
+ mEdgeGlowTop.getMaxHeight() + getPaddingTop());
}
canvas.restoreToCount(restoreCount);
}
@@ -4040,9 +4054,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
canvas.rotate(180, width, 0);
mEdgeGlowBottom.setSize(width, height);
if (mEdgeGlowBottom.draw(canvas)) {
- // Account for the rotation
- mEdgeGlowBottom.setPosition(edgeX + width, edgeY);
- invalidate(mEdgeGlowBottom.getBounds(true));
+ invalidate(0, getHeight() - getPaddingBottom() -
+ mEdgeGlowBottom.getMaxHeight(), getWidth(),
+ getHeight());
}
canvas.restoreToCount(restoreCount);
}
@@ -4161,7 +4175,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final int y = (int) ev.getY(pointerIndex);
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
- if (startScrollIfNeeded(y, null)) {
+ if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) {
return true;
}
break;
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 225cd6d..4f2d9c6 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -19,7 +19,9 @@ package android.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Rect;
+import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
@@ -32,9 +34,12 @@ import android.view.accessibility.AccessibilityNodeInfo;
import com.android.internal.R;
public abstract class AbsSeekBar extends ProgressBar {
+ private final Rect mTempRect = new Rect();
+
private Drawable mThumb;
private int mThumbOffset;
-
+ private boolean mSplitTrack;
+
/**
* On touch, this offset plus the scaled value from the position of the
* touch will form the progress value. Usually 0.
@@ -51,10 +56,10 @@ public abstract class AbsSeekBar extends ProgressBar {
* progress.
*/
private int mKeyProgressIncrement = 1;
-
+
private static final int NO_ALPHA = 0xFF;
private float mDisabledAlpha;
-
+
private int mScaledTouchSlop;
private float mTouchDownX;
private boolean mIsDragging;
@@ -76,12 +81,16 @@ public abstract class AbsSeekBar extends ProgressBar {
TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.SeekBar, defStyleAttr, defStyleRes);
- Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
- setThumb(thumb); // will guess mThumbOffset if thumb != null...
- // ...but allow layout to override this
- int thumbOffset = a.getDimensionPixelOffset(
+
+ final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
+ setThumb(thumb);
+
+ // Guess thumb offset if thumb != null, but allow layout to override.
+ final int thumbOffset = a.getDimensionPixelOffset(
com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset());
setThumbOffset(thumbOffset);
+
+ mSplitTrack = a.getBoolean(com.android.internal.R.styleable.SeekBar_splitTrack, false);
a.recycle();
a = context.obtainStyledAttributes(attrs,
@@ -97,7 +106,7 @@ public abstract class AbsSeekBar extends ProgressBar {
* <p>
* If the thumb is a valid drawable (i.e. not null), half its width will be
* used as the new thumb offset (@see #setThumbOffset(int)).
- *
+ *
* @param thumb Drawable representing the thumb
*/
public void setThumb(Drawable thumb) {
@@ -132,7 +141,7 @@ public abstract class AbsSeekBar extends ProgressBar {
mThumb = thumb;
invalidate();
if (needUpdate) {
- updateThumbPos(getWidth(), getHeight());
+ updateThumbAndTrackPos(getWidth(), getHeight());
if (thumb != null && thumb.isStateful()) {
// Note that if the states are different this won't work.
// For now, let's consider that an app bug.
@@ -162,7 +171,7 @@ public abstract class AbsSeekBar extends ProgressBar {
/**
* Sets the thumb offset that allows the thumb to extend out of the range of
* the track.
- *
+ *
* @param thumbOffset The offset amount in pixels.
*/
public void setThumbOffset(int thumbOffset) {
@@ -171,8 +180,27 @@ public abstract class AbsSeekBar extends ProgressBar {
}
/**
+ * Specifies whether the track should be split by the thumb. When true,
+ * the thumb's optical bounds will be clipped out of the track drawable,
+ * then the thumb will be drawn into the resulting gap.
+ *
+ * @param splitTrack Whether the track should be split by the thumb
+ */
+ public void setSplitTrack(boolean splitTrack) {
+ mSplitTrack = splitTrack;
+ invalidate();
+ }
+
+ /**
+ * Returns whether the track should be split by the thumb.
+ */
+ public boolean getSplitTrack() {
+ return mSplitTrack;
+ }
+
+ /**
* Sets the amount of progress changed via the arrow keys.
- *
+ *
* @param increment The amount to increment or decrement when the user
* presses the arrow keys.
*/
@@ -184,14 +212,14 @@ public abstract class AbsSeekBar extends ProgressBar {
* Returns the amount of progress changed via the arrow keys.
* <p>
* By default, this will be a value that is derived from the max progress.
- *
+ *
* @return The amount to increment or decrement when the user presses the
* arrow keys. This will be positive.
*/
public int getKeyProgressIncrement() {
return mKeyProgressIncrement;
}
-
+
@Override
public synchronized void setMax(int max) {
super.setMax(max);
@@ -217,79 +245,85 @@ public abstract class AbsSeekBar extends ProgressBar {
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
-
- Drawable progressDrawable = getProgressDrawable();
+
+ final Drawable progressDrawable = getProgressDrawable();
if (progressDrawable != null) {
progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
}
-
- if (mThumb != null && mThumb.isStateful()) {
- int[] state = getDrawableState();
- mThumb.setState(state);
+
+ final Drawable thumb = mThumb;
+ if (thumb != null && thumb.isStateful()) {
+ thumb.setState(getDrawableState());
}
}
-
+
@Override
void onProgressRefresh(float scale, boolean fromUser) {
super.onProgressRefresh(scale, fromUser);
- Drawable thumb = mThumb;
+
+ final Drawable thumb = mThumb;
if (thumb != null) {
setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
- /*
- * Since we draw translated, the drawable's bounds that it signals
- * for invalidation won't be the actual bounds we want invalidated,
- * so just invalidate this whole view.
- */
+
+ // Since we draw translated, the drawable's bounds that it signals
+ // for invalidation won't be the actual bounds we want invalidated,
+ // so just invalidate this whole view.
invalidate();
}
}
-
-
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
- updateThumbPos(w, h);
+
+ updateThumbAndTrackPos(w, h);
}
- private void updateThumbPos(int w, int h) {
- Drawable d = getCurrentDrawable();
- Drawable thumb = mThumb;
- int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
+ private void updateThumbAndTrackPos(int w, int h) {
+ final Drawable track = getCurrentDrawable();
+ final Drawable thumb = mThumb;
+
// The max height does not incorporate padding, whereas the height
- // parameter does
- int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom);
-
- int max = getMax();
- float scale = max > 0 ? (float) getProgress() / (float) max : 0;
-
+ // parameter does.
+ final int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom);
+ final int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
+
+ // Apply offset to whichever item is taller.
+ final int trackOffset;
+ final int thumbOffset;
if (thumbHeight > trackHeight) {
- if (thumb != null) {
- setThumbPos(w, thumb, scale, 0);
- }
- int gapForCenteringTrack = (thumbHeight - trackHeight) / 2;
- if (d != null) {
- // Canvas will be translated by the padding, so 0,0 is where we start drawing
- d.setBounds(0, gapForCenteringTrack,
- w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - gapForCenteringTrack
- - mPaddingTop);
- }
+ trackOffset = (thumbHeight - trackHeight) / 2;
+ thumbOffset = 0;
} else {
- if (d != null) {
- // Canvas will be translated by the padding, so 0,0 is where we start drawing
- d.setBounds(0, 0, w - mPaddingRight - mPaddingLeft, h - mPaddingBottom
- - mPaddingTop);
- }
- int gap = (trackHeight - thumbHeight) / 2;
- if (thumb != null) {
- setThumbPos(w, thumb, scale, gap);
- }
+ trackOffset = 0;
+ thumbOffset = (trackHeight - thumbHeight) / 2;
+ }
+
+ if (track != null) {
+ track.setBounds(0, trackOffset, w - mPaddingRight - mPaddingLeft,
+ h - mPaddingBottom - trackOffset - mPaddingTop);
+ }
+
+ if (thumb != null) {
+ setThumbPos(w, thumb, getScale(), thumbOffset);
}
}
+ private float getScale() {
+ final int max = getMax();
+ return max > 0 ? getProgress() / (float) max : 0;
+ }
+
/**
- * @param gap If set to {@link Integer#MIN_VALUE}, this will be ignored and
+ * Updates the thumb drawable bounds.
+ *
+ * @param w Width of the view, including padding
+ * @param thumb Drawable used for the thumb
+ * @param scale Current progress between 0 and 1
+ * @param offset Vertical offset for centering. If set to
+ * {@link Integer#MIN_VALUE}, the current offset will be used.
*/
- private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
+ private void setThumbPos(int w, Drawable thumb, float scale, int offset) {
int available = w - mPaddingLeft - mPaddingRight;
final int thumbWidth = thumb.getIntrinsicWidth();
final int thumbHeight = thumb.getIntrinsicHeight();
@@ -301,13 +335,13 @@ public abstract class AbsSeekBar extends ProgressBar {
final int thumbPos = (int) (scale * available + 0.5f);
final int top, bottom;
- if (gap == Integer.MIN_VALUE) {
+ if (offset == Integer.MIN_VALUE) {
final Rect oldBounds = thumb.getBounds();
top = oldBounds.top;
bottom = oldBounds.bottom;
} else {
- top = gap;
- bottom = gap + thumbHeight;
+ top = offset;
+ bottom = offset + thumbHeight;
}
final int left = (isLayoutRtl() && mMirrorForRtl) ? available - thumbPos : thumbPos;
@@ -342,6 +376,33 @@ public abstract class AbsSeekBar extends ProgressBar {
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
+ drawThumb(canvas);
+ }
+
+ @Override
+ void drawTrack(Canvas canvas) {
+ final Drawable thumbDrawable = mThumb;
+ if (thumbDrawable != null && mSplitTrack) {
+ final Insets insets = thumbDrawable.getOpticalInsets();
+ final Rect tempRect = mTempRect;
+ thumbDrawable.copyBounds(tempRect);
+ tempRect.offset(mPaddingLeft - mThumbOffset, mPaddingTop);
+ tempRect.left += insets.left;
+ tempRect.right -= insets.right;
+
+ final int saveCount = canvas.save();
+ canvas.clipRect(tempRect, Op.DIFFERENCE);
+ super.drawTrack(canvas);
+ canvas.restoreToCount(saveCount);
+ } else {
+ super.drawTrack(canvas);
+ }
+ }
+
+ /**
+ * Draw the thumb.
+ */
+ void drawThumb(Canvas canvas) {
if (mThumb != null) {
canvas.save();
// Translate the padding. For the x, we need to allow the thumb to
@@ -366,17 +427,17 @@ public abstract class AbsSeekBar extends ProgressBar {
}
dw += mPaddingLeft + mPaddingRight;
dh += mPaddingTop + mPaddingBottom;
-
+
setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
resolveSizeAndState(dh, heightMeasureSpec, 0));
}
-
+
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mIsUserSeekable || !isEnabled()) {
return false;
}
-
+
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (isInScrollingContainer()) {
@@ -391,7 +452,7 @@ public abstract class AbsSeekBar extends ProgressBar {
attemptClaimDrag();
}
break;
-
+
case MotionEvent.ACTION_MOVE:
if (mIsDragging) {
trackTouchEvent(event);
@@ -408,7 +469,7 @@ public abstract class AbsSeekBar extends ProgressBar {
}
}
break;
-
+
case MotionEvent.ACTION_UP:
if (mIsDragging) {
trackTouchEvent(event);
@@ -426,7 +487,7 @@ public abstract class AbsSeekBar extends ProgressBar {
// value has not apparently changed)
invalidate();
break;
-
+
case MotionEvent.ACTION_CANCEL:
if (mIsDragging) {
onStopTrackingTouch();
@@ -493,7 +554,7 @@ public abstract class AbsSeekBar extends ProgressBar {
mParent.requestDisallowInterceptTouchEvent(true);
}
}
-
+
/**
* This is called when the user has started touching this widget.
*/
@@ -526,7 +587,7 @@ public abstract class AbsSeekBar extends ProgressBar {
setProgress(progress - mKeyProgressIncrement, true);
onKeyChange();
return true;
-
+
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (progress >= getMax()) break;
setProgress(progress + mKeyProgressIncrement, true);
@@ -595,17 +656,13 @@ public abstract class AbsSeekBar extends ProgressBar {
public void onRtlPropertiesChanged(int layoutDirection) {
super.onRtlPropertiesChanged(layoutDirection);
- int max = getMax();
- float scale = max > 0 ? (float) getProgress() / (float) max : 0;
-
- Drawable thumb = mThumb;
+ final Drawable thumb = mThumb;
if (thumb != null) {
- setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
- /*
- * Since we draw translated, the drawable's bounds that it signals
- * for invalidation won't be the actual bounds we want invalidated,
- * so just invalidate this whole view.
- */
+ setThumbPos(getWidth(), thumb, getScale(), Integer.MIN_VALUE);
+
+ // Since we draw translated, the drawable's bounds that it signals
+ // for invalidation won't be the actual bounds we want invalidated,
+ // so just invalidate this whole view.
invalidate();
}
}
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index fa37443..83fbe8f 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -16,7 +16,14 @@
package android.widget;
+import android.content.res.TypedArray;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Xfermode;
+import android.util.Log;
import com.android.internal.R;
import android.content.Context;
@@ -59,12 +66,10 @@ public class EdgeEffect {
private static final int PULL_DECAY_TIME = 1000;
private static final float MAX_ALPHA = 1.f;
- private static final float HELD_EDGE_SCALE_Y = 0.5f;
- private static final float MAX_GLOW_HEIGHT = 4.f;
+ private static final float MAX_GLOW_HEIGHT = 1.5f;
- private static final float PULL_GLOW_BEGIN = 1.f;
- private static final float PULL_EDGE_BEGIN = 0.6f;
+ private static final float PULL_GLOW_BEGIN = 0.f;
// Minimum velocity that will be absorbed
private static final int MIN_VELOCITY = 100;
@@ -73,24 +78,11 @@ public class EdgeEffect {
private static final float EPSILON = 0.001f;
- private final Drawable mEdge;
- private final Drawable mGlow;
- private int mWidth;
- private int mHeight;
- private int mX;
- private int mY;
- private static final int MIN_WIDTH = 300;
- private final int mMinWidth;
-
- private float mEdgeAlpha;
- private float mEdgeScaleY;
+ private static final float SIN_45 = (float) Math.sin(Math.PI / 4);
+
private float mGlowAlpha;
private float mGlowScaleY;
- private float mEdgeAlphaStart;
- private float mEdgeAlphaFinish;
- private float mEdgeScaleYStart;
- private float mEdgeScaleYFinish;
private float mGlowAlphaStart;
private float mGlowAlphaFinish;
private float mGlowScaleYStart;
@@ -107,16 +99,11 @@ public class EdgeEffect {
private static final int STATE_RECEDE = 3;
private static final int STATE_PULL_DECAY = 4;
- // How much dragging should effect the height of the edge image.
- // Number determined by user testing.
- private static final int PULL_DISTANCE_EDGE_FACTOR = 7;
-
// How much dragging should effect the height of the glow image.
// Number determined by user testing.
private static final int PULL_DISTANCE_GLOW_FACTOR = 7;
private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 1.1f;
- private static final int VELOCITY_EDGE_FACTOR = 8;
private static final int VELOCITY_GLOW_FACTOR = 12;
private int mState = STATE_IDLE;
@@ -124,30 +111,26 @@ public class EdgeEffect {
private float mPullDistance;
private final Rect mBounds = new Rect();
-
- private final int mEdgeHeight;
- private final int mGlowHeight;
- private final int mGlowWidth;
- private final int mMaxEffectHeight;
+ private final RectF mArcRect = new RectF();
+ private final Paint mPaint = new Paint();
+ private float mRadius;
+ private float mDisplacement = 0.5f;
+ private float mTargetDisplacement = 0.5f;
/**
* Construct a new EdgeEffect with a theme appropriate for the provided context.
* @param context Context used to provide theming and resource information for the EdgeEffect
*/
public EdgeEffect(Context context) {
- final Resources res = context.getResources();
- mEdge = context.getDrawable(R.drawable.overscroll_edge);
- mGlow = context.getDrawable(R.drawable.overscroll_glow);
-
- mEdgeHeight = mEdge.getIntrinsicHeight();
- mGlowHeight = mGlow.getIntrinsicHeight();
- mGlowWidth = mGlow.getIntrinsicWidth();
-
- mMaxEffectHeight = (int) (Math.min(
- mGlowHeight * MAX_GLOW_HEIGHT * mGlowHeight / mGlowWidth * 0.6f,
- mGlowHeight * MAX_GLOW_HEIGHT) + 0.5f);
-
- mMinWidth = (int) (res.getDisplayMetrics().density * MIN_WIDTH + 0.5f);
+ mPaint.setAntiAlias(true);
+ final TypedArray a = context.obtainStyledAttributes(
+ com.android.internal.R.styleable.EdgeEffect);
+ final int themeColor = a.getColor(
+ com.android.internal.R.styleable.EdgeEffect_colorPrimaryLight, 0xff666666);
+ a.recycle();
+ mPaint.setColor((themeColor & 0xffffff) | 0x66000000);
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
mInterpolator = new DecelerateInterpolator();
}
@@ -158,20 +141,12 @@ public class EdgeEffect {
* @param height Effect height in pixels
*/
public void setSize(int width, int height) {
- mWidth = width;
- mHeight = height;
- }
+ final float r = width * 0.5f / SIN_45;
+ final float y = SIN_45 * r;
+ final float h = r - y;
+ mRadius = r;
- /**
- * Set the position of this edge effect in pixels. This position is
- * only used by {@link #getBounds(boolean)}.
- *
- * @param x The position of the edge effect on the X axis
- * @param y The position of the edge effect on the Y axis
- */
- void setPosition(int x, int y) {
- mX = x;
- mY = y;
+ mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
}
/**
@@ -199,17 +174,38 @@ public class EdgeEffect {
* The host view should always {@link android.view.View#invalidate()} after this
* and draw the results accordingly.
*
+ * <p>Views using EdgeEffect should favor {@link #onPull(float, float)} when the displacement
+ * of the pull point is known.</p>
+ *
* @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
* 1.f (full length of the view) or negative values to express change
* back toward the edge reached to initiate the effect.
*/
public void onPull(float deltaDistance) {
+ onPull(deltaDistance, 0.5f);
+ }
+
+ /**
+ * A view should call this when content is pulled away from an edge by the user.
+ * This will update the state of the current visual effect and its associated animation.
+ * The host view should always {@link android.view.View#invalidate()} after this
+ * and draw the results accordingly.
+ *
+ * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
+ * 1.f (full length of the view) or negative values to express change
+ * back toward the edge reached to initiate the effect.
+ * @param displacement The displacement from the starting side of the effect of the point
+ * initiating the pull. In the case of touch this is the finger position.
+ * Values may be from 0-1.
+ */
+ public void onPull(float deltaDistance, float displacement) {
final long now = AnimationUtils.currentAnimationTimeMillis();
+ mTargetDisplacement = displacement;
if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
return;
}
if (mState != STATE_PULL) {
- mGlowScaleY = PULL_GLOW_BEGIN;
+ mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY);
}
mState = STATE_PULL;
@@ -217,15 +213,10 @@ public class EdgeEffect {
mDuration = PULL_TIME;
mPullDistance += deltaDistance;
- float distance = Math.abs(mPullDistance);
-
- mEdgeAlpha = mEdgeAlphaStart = Math.max(PULL_EDGE_BEGIN, Math.min(distance, MAX_ALPHA));
- mEdgeScaleY = mEdgeScaleYStart = Math.max(
- HELD_EDGE_SCALE_Y, Math.min(distance * PULL_DISTANCE_EDGE_FACTOR, 1.f));
mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
mGlowAlpha +
- (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
+ (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
float glowChange = Math.abs(deltaDistance);
if (deltaDistance > 0 && mPullDistance < 0) {
@@ -239,8 +230,6 @@ public class EdgeEffect {
mGlowScaleY = mGlowScaleYStart = Math.min(MAX_GLOW_HEIGHT, Math.max(
0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR));
- mEdgeAlphaFinish = mEdgeAlpha;
- mEdgeScaleYFinish = mEdgeScaleY;
mGlowAlphaFinish = mGlowAlpha;
mGlowScaleYFinish = mGlowScaleY;
}
@@ -259,13 +248,9 @@ public class EdgeEffect {
}
mState = STATE_RECEDE;
- mEdgeAlphaStart = mEdgeAlpha;
- mEdgeScaleYStart = mEdgeScaleY;
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
- mEdgeAlphaFinish = 0.f;
- mEdgeScaleYFinish = 0.f;
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
@@ -290,30 +275,21 @@ public class EdgeEffect {
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = 0.15f + (velocity * 0.02f);
- // The edge should always be at least partially visible, regardless
- // of velocity.
- mEdgeAlphaStart = 0.f;
- mEdgeScaleY = mEdgeScaleYStart = 0.f;
// The glow depends more on the velocity, and therefore starts out
// nearly invisible.
mGlowAlphaStart = 0.3f;
- mGlowScaleYStart = 0.f;
+ mGlowScaleYStart = Math.max(mGlowScaleY, 0.f);
- // Factor the velocity by 8. Testing on device shows this works best to
- // reflect the strength of the user's scrolling.
- mEdgeAlphaFinish = Math.max(0, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1));
- // Edge should never get larger than the size of its asset.
- mEdgeScaleYFinish = Math.max(
- HELD_EDGE_SCALE_Y, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1.f));
// Growth for the size of the glow should be quadratic to properly
// respond
// to a user's scrolling speed. The faster the scrolling speed, the more
// intense the effect should be for both the size and the saturation.
- mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
+ mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2, 1.f);
// Alpha should change for the glow as well as size.
mGlowAlphaFinish = Math.max(
mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
+ mTargetDisplacement = 0.5f;
}
@@ -330,52 +306,42 @@ public class EdgeEffect {
public boolean draw(Canvas canvas) {
update();
- mGlow.setAlpha((int) (Math.max(0, Math.min(mGlowAlpha, 1)) * 255));
-
- int glowBottom = (int) Math.min(
- mGlowHeight * mGlowScaleY * mGlowHeight / mGlowWidth * 0.6f,
- mGlowHeight * MAX_GLOW_HEIGHT);
- if (mWidth < mMinWidth) {
- // Center the glow and clip it.
- int glowLeft = (mWidth - mMinWidth)/2;
- mGlow.setBounds(glowLeft, 0, mWidth - glowLeft, glowBottom);
- } else {
- // Stretch the glow to fit.
- mGlow.setBounds(0, 0, mWidth, glowBottom);
- }
+ final int count = canvas.save();
- mGlow.draw(canvas);
+ final float y = mBounds.height();
+ final float centerY = y - mRadius;
+ final float centerX = mBounds.centerX();
+ mArcRect.set(centerX - mRadius, centerY - mRadius, centerX + mRadius, centerY + mRadius);
+ canvas.scale(1.f, Math.min(mGlowScaleY, 1.f), centerX, 0);
- mEdge.setAlpha((int) (Math.max(0, Math.min(mEdgeAlpha, 1)) * 255));
-
- int edgeBottom = (int) (mEdgeHeight * mEdgeScaleY);
- if (mWidth < mMinWidth) {
- // Center the edge and clip it.
- int edgeLeft = (mWidth - mMinWidth)/2;
- mEdge.setBounds(edgeLeft, 0, mWidth - edgeLeft, edgeBottom);
- } else {
- // Stretch the edge to fit.
- mEdge.setBounds(0, 0, mWidth, edgeBottom);
+ final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
+ float translateX = mBounds.width() * displacement;
+ float translateY = 0;
+ if (mGlowScaleY > 1.f) {
+ translateY = (mGlowScaleY - 1.f) * mBounds.height();
}
- mEdge.draw(canvas);
-
- if (mState == STATE_RECEDE && glowBottom == 0 && edgeBottom == 0) {
+ canvas.clipRect(Float.MIN_VALUE, mBounds.top,
+ Float.MAX_VALUE, Float.MAX_VALUE);
+ canvas.translate(translateX, translateY);
+ canvas.drawArc(mArcRect, 0, 180, true, mPaint);
+ canvas.restoreToCount(count);
+
+ boolean oneLastFrame = false;
+ if (mState == STATE_RECEDE && mGlowScaleY == 0) {
mState = STATE_IDLE;
+ oneLastFrame = true;
}
- return mState != STATE_IDLE;
+ return mState != STATE_IDLE || oneLastFrame;
}
/**
- * Returns the bounds of the edge effect.
- *
- * @hide
+ * Return the maximum height that the edge effect will be drawn at given the original
+ * {@link #setSize(int, int) input size}.
+ * @return The maximum height of the edge effect
*/
- public Rect getBounds(boolean reverse) {
- mBounds.set(0, 0, mWidth, mMaxEffectHeight);
- mBounds.offset(mX, mY - (reverse ? mMaxEffectHeight : 0));
-
- return mBounds;
+ public int getMaxHeight() {
+ return (int) (mBounds.height() * MAX_GLOW_HEIGHT + 0.5f);
}
private void update() {
@@ -384,10 +350,9 @@ public class EdgeEffect {
final float interp = mInterpolator.getInterpolation(t);
- mEdgeAlpha = mEdgeAlphaStart + (mEdgeAlphaFinish - mEdgeAlphaStart) * interp;
- mEdgeScaleY = mEdgeScaleYStart + (mEdgeScaleYFinish - mEdgeScaleYStart) * interp;
mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
+ mDisplacement = (mDisplacement + mTargetDisplacement) / 2;
if (t >= 1.f - EPSILON) {
switch (mState) {
@@ -396,14 +361,10 @@ public class EdgeEffect {
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = RECEDE_TIME;
- mEdgeAlphaStart = mEdgeAlpha;
- mEdgeScaleYStart = mEdgeScaleY;
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
- // After absorb, the glow and edge should fade to nothing.
- mEdgeAlphaFinish = 0.f;
- mEdgeScaleYFinish = 0.f;
+ // After absorb, the glow should fade to nothing.
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
break;
@@ -412,26 +373,14 @@ public class EdgeEffect {
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mDuration = PULL_DECAY_TIME;
- mEdgeAlphaStart = mEdgeAlpha;
- mEdgeScaleYStart = mEdgeScaleY;
mGlowAlphaStart = mGlowAlpha;
mGlowScaleYStart = mGlowScaleY;
- // After pull, the glow and edge should fade to nothing.
- mEdgeAlphaFinish = 0.f;
- mEdgeScaleYFinish = 0.f;
+ // After pull, the glow should fade to nothing.
mGlowAlphaFinish = 0.f;
mGlowScaleYFinish = 0.f;
break;
case STATE_PULL_DECAY:
- // When receding, we want edge to decrease more slowly
- // than the glow.
- float factor = mGlowScaleYFinish != 0 ? 1
- / (mGlowScaleYFinish * mGlowScaleYFinish)
- : Float.MAX_VALUE;
- mEdgeScaleY = mEdgeScaleYStart +
- (mEdgeScaleYFinish - mEdgeScaleYStart) *
- interp * factor;
mState = STATE_RECEDE;
break;
case STATE_RECEDE:
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 25d4f42..0c65c50 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -616,12 +616,14 @@ public class HorizontalScrollView extends FrameLayout {
if (canOverscroll) {
final int pulledToX = oldX + deltaX;
if (pulledToX < 0) {
- mEdgeGlowLeft.onPull((float) deltaX / getWidth());
+ mEdgeGlowLeft.onPull((float) deltaX / getWidth(),
+ 1.f - ev.getY(activePointerIndex) / getHeight());
if (!mEdgeGlowRight.isFinished()) {
mEdgeGlowRight.onRelease();
}
} else if (pulledToX > range) {
- mEdgeGlowRight.onPull((float) deltaX / getWidth());
+ mEdgeGlowRight.onPull((float) deltaX / getWidth(),
+ ev.getY(activePointerIndex) / getHeight());
if (!mEdgeGlowLeft.isFinished()) {
mEdgeGlowLeft.onRelease();
}
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index f7e81b8..0c3715d 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -1066,21 +1066,30 @@ public class ProgressBar extends View {
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
- Drawable d = mCurrentDrawable;
+ drawTrack(canvas);
+ }
+
+ /**
+ * Draws the progress bar track.
+ */
+ void drawTrack(Canvas canvas) {
+ final Drawable d = mCurrentDrawable;
if (d != null) {
// Translate canvas so a indeterminate circular progress bar with padding
// rotates properly in its animation
- canvas.save();
+ final int saveCount = canvas.save();
+
if(isLayoutRtl() && mMirrorForRtl) {
canvas.translate(getWidth() - mPaddingRight, mPaddingTop);
canvas.scale(-1.0f, 1.0f);
} else {
canvas.translate(mPaddingLeft, mPaddingTop);
}
- long time = getDrawingTime();
+
+ final long time = getDrawingTime();
if (mHasAnimation) {
mAnimation.getTransformation(time, mTransformation);
- float scale = mTransformation.getAlpha();
+ final float scale = mTransformation.getAlpha();
try {
mInDrawing = true;
d.setLevel((int) (scale * MAX_LEVEL));
@@ -1089,8 +1098,10 @@ public class ProgressBar extends View {
}
postInvalidateOnAnimation();
}
+
d.draw(canvas);
- canvas.restore();
+ canvas.restoreToCount(saveCount);
+
if (mShouldStartAnimationDrawable && d instanceof Animatable) {
((Animatable) d).start();
mShouldStartAnimationDrawable = false;
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 0fa75a6..fd04890 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -669,12 +669,14 @@ public class ScrollView extends FrameLayout {
} else if (canOverscroll) {
final int pulledToY = oldY + deltaY;
if (pulledToY < 0) {
- mEdgeGlowTop.onPull((float) deltaY / getHeight());
+ mEdgeGlowTop.onPull((float) deltaY / getHeight(),
+ ev.getX(activePointerIndex) / getWidth());
if (!mEdgeGlowBottom.isFinished()) {
mEdgeGlowBottom.onRelease();
}
} else if (pulledToY > range) {
- mEdgeGlowBottom.onPull((float) deltaY / getHeight());
+ mEdgeGlowBottom.onPull((float) deltaY / getHeight(),
+ 1.f - ev.getX(activePointerIndex) / getWidth());
if (!mEdgeGlowTop.isFinished()) {
mEdgeGlowTop.onRelease();
}
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 08af4de..438e164 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -22,9 +22,11 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
+import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.StaticLayout;
@@ -85,6 +87,7 @@ public class Switch extends CompoundButton {
private int mThumbTextPadding;
private int mSwitchMinWidth;
private int mSwitchPadding;
+ private boolean mSplitTrack;
private CharSequence mTextOn;
private CharSequence mTextOff;
@@ -174,13 +177,13 @@ public class Switch extends CompoundButton {
super(context, attrs, defStyleAttr, defStyleRes);
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
- Resources res = getResources();
+
+ final Resources res = getResources();
mTextPaint.density = res.getDisplayMetrics().density;
mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.Switch, defStyleAttr, defStyleRes);
-
mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb);
mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track);
mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn);
@@ -191,15 +194,16 @@ public class Switch extends CompoundButton {
com.android.internal.R.styleable.Switch_switchMinWidth, 0);
mSwitchPadding = a.getDimensionPixelSize(
com.android.internal.R.styleable.Switch_switchPadding, 0);
+ mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false);
- int appearance = a.getResourceId(
+ final int appearance = a.getResourceId(
com.android.internal.R.styleable.Switch_switchTextAppearance, 0);
if (appearance != 0) {
setSwitchTextAppearance(context, appearance);
}
a.recycle();
- ViewConfiguration config = ViewConfiguration.get(context);
+ final ViewConfiguration config = ViewConfiguration.get(context);
mTouchSlop = config.getScaledTouchSlop();
mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
@@ -469,6 +473,29 @@ public class Switch extends CompoundButton {
}
/**
+ * Specifies whether the track should be split by the thumb. When true,
+ * the thumb's optical bounds will be clipped out of the track drawable,
+ * then the thumb will be drawn into the resulting gap.
+ *
+ * @param splitTrack Whether the track should be split by the thumb
+ *
+ * @attr ref android.R.styleable#Switch_splitTrack
+ */
+ public void setSplitTrack(boolean splitTrack) {
+ mSplitTrack = splitTrack;
+ invalidate();
+ }
+
+ /**
+ * Returns whether the track should be split by the thumb.
+ *
+ * @attr ref android.R.styleable#Switch_splitTrack
+ */
+ public boolean getSplitTrack() {
+ return mSplitTrack;
+ }
+
+ /**
* Returns the text displayed when the button is in the checked state.
*
* @attr ref android.R.styleable#Switch_textOn
@@ -518,13 +545,15 @@ public class Switch extends CompoundButton {
mTrackDrawable.getPadding(mTempRect);
- final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());
+ final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
+ + mThumbTextPadding * 2;
+ mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth());
+
final int switchWidth = Math.max(mSwitchMinWidth,
- maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);
+ 2 * mThumbWidth + mTempRect.left + mTempRect.right);
final int switchHeight = Math.max(mTrackDrawable.getIntrinsicHeight(),
mThumbDrawable.getIntrinsicHeight());
- mThumbWidth = maxTextWidth + mThumbTextPadding * 2;
mSwitchWidth = switchWidth;
mSwitchHeight = switchHeight;
@@ -777,7 +806,7 @@ public class Switch extends CompoundButton {
final Drawable trackDrawable = mTrackDrawable;
final Drawable thumbDrawable = mThumbDrawable;
- // Draw the switch
+ // Layout the track.
final int switchLeft = mSwitchLeft;
final int switchTop = mSwitchTop;
final int switchRight = mSwitchRight;
@@ -793,9 +822,10 @@ public class Switch extends CompoundButton {
// Relies on mTempRect, MUST be called first!
final int thumbPos = getThumbOffset();
+ // Layout the thumb.
thumbDrawable.getPadding(tempRect);
- int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
- int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
+ final int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
+ final int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
final Drawable background = getBackground();
@@ -805,20 +835,32 @@ public class Switch extends CompoundButton {
super.onDraw(canvas);
- trackDrawable.draw(canvas);
+ if (mSplitTrack) {
+ final Insets insets = thumbDrawable.getOpticalInsets();
+ thumbDrawable.copyBounds(tempRect);
+ tempRect.left += insets.left;
+ tempRect.right -= insets.right;
+
+ final int saveCount = canvas.save();
+ canvas.clipRect(tempRect, Op.DIFFERENCE);
+ trackDrawable.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ } else {
+ trackDrawable.draw(canvas);
+ }
final int saveCount = canvas.save();
canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
thumbDrawable.draw(canvas);
- final int drawableState[] = getDrawableState();
- if (mTextColors != null) {
- mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
- }
- mTextPaint.drawableState = drawableState;
-
final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
if (switchText != null) {
+ final int drawableState[] = getDrawableState();
+ if (mTextColors != null) {
+ mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
+ }
+ mTextPaint.drawableState = drawableState;
+
final int left = (thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2;
final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2;
canvas.translate(left, top);
@@ -889,12 +931,16 @@ public class Switch extends CompoundButton {
protected void drawableStateChanged() {
super.drawableStateChanged();
- int[] myDrawableState = getDrawableState();
+ final int[] myDrawableState = getDrawableState();
+
+ if (mThumbDrawable != null && mThumbDrawable.setState(myDrawableState)) {
+ // Handle changes to thumb width and height.
+ requestLayout();
+ }
- // Set the state of the Drawable
- // Drawable may be null when checked state is set from XML, from super constructor
- if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState);
- if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState);
+ if (mTrackDrawable != null) {
+ mTrackDrawable.setState(myDrawableState);
+ }
invalidate();
}
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 4726da7..b568121 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -26,6 +26,7 @@ import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
@@ -38,6 +39,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
@@ -240,6 +242,7 @@ public class AlertController {
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
mWindow.setContentView(mAlertDialogLayout);
setupView();
+ setupDecor();
}
public void setTitle(CharSequence title) {
@@ -415,7 +418,28 @@ public class AlertController {
public boolean onKeyUp(int keyCode, KeyEvent event) {
return mScrollView != null && mScrollView.executeKeyEvent(event);
}
-
+
+ private void setupDecor() {
+ final View decor = mWindow.getDecorView();
+ final View parent = mWindow.findViewById(R.id.parentPanel);
+ if (parent != null && decor != null) {
+ decor.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+ @Override
+ public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
+ if (insets.isRound()) {
+ // TODO: Get the padding as a function of the window size.
+ int roundOffset = mContext.getResources().getDimensionPixelOffset(
+ R.dimen.alert_dialog_round_padding);
+ parent.setPadding(roundOffset, roundOffset, roundOffset, roundOffset);
+ }
+ return insets.consumeSystemWindowInsets();
+ }
+ });
+ decor.setFitsSystemWindows(true);
+ decor.requestApplyInsets();
+ }
+ }
+
private void setupView() {
LinearLayout contentPanel = (LinearLayout) mWindow.findViewById(R.id.contentPanel);
setupContent(contentPanel);
@@ -636,14 +660,31 @@ public class AlertController {
private void setBackground(TypedArray a, View topPanel, View contentPanel, View customPanel,
View buttonPanel, boolean hasTitle, boolean hasCustomView, boolean hasButtons) {
- final int topBright = a.getResourceId(
- R.styleable.AlertDialog_topBright, R.drawable.popup_top_bright);
- final int topDark = a.getResourceId(
- R.styleable.AlertDialog_topDark, R.drawable.popup_top_dark);
- final int centerBright = a.getResourceId(
- R.styleable.AlertDialog_centerBright, R.drawable.popup_center_bright);
- final int centerDark = a.getResourceId(
- R.styleable.AlertDialog_centerDark, R.drawable.popup_center_dark);
+ int fullDark = 0;
+ int topDark = 0;
+ int centerDark = 0;
+ int bottomDark = 0;
+ int fullBright = 0;
+ int topBright = 0;
+ int centerBright = 0;
+ int bottomBright = 0;
+ int bottomMedium = 0;
+ if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.KITKAT) {
+ fullDark = R.drawable.popup_full_dark;
+ topDark = R.drawable.popup_top_dark;
+ centerDark = R.drawable.popup_center_dark;
+ bottomDark = R.drawable.popup_bottom_dark;
+ fullBright = R.drawable.popup_full_bright;
+ topBright = R.drawable.popup_top_bright;
+ centerBright = R.drawable.popup_center_bright;
+ bottomBright = R.drawable.popup_bottom_bright;
+ bottomMedium = R.drawable.popup_bottom_medium;
+ }
+ topBright = a.getResourceId(R.styleable.AlertDialog_topBright, topBright);
+ topDark = a.getResourceId(R.styleable.AlertDialog_topDark, topDark);
+ centerBright = a.getResourceId(R.styleable.AlertDialog_centerBright, centerBright);
+ centerDark = a.getResourceId(R.styleable.AlertDialog_centerDark, centerDark);
+
/* We now set the background of all of the sections of the alert.
* First collect together each section that is being displayed along
@@ -707,22 +748,17 @@ public class AlertController {
if (lastView != null) {
if (setView) {
- final int bottomBright = a.getResourceId(
- R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright);
- final int bottomMedium = a.getResourceId(
- R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium);
- final int bottomDark = a.getResourceId(
- R.styleable.AlertDialog_bottomDark, R.drawable.popup_bottom_dark);
+ bottomBright = a.getResourceId(R.styleable.AlertDialog_bottomBright, bottomBright);
+ bottomMedium = a.getResourceId(R.styleable.AlertDialog_bottomMedium, bottomMedium);
+ bottomDark = a.getResourceId(R.styleable.AlertDialog_bottomDark, bottomDark);
// ListViews will use the Bright background, but buttons use the
// Medium background.
lastView.setBackgroundResource(
lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark);
} else {
- final int fullBright = a.getResourceId(
- R.styleable.AlertDialog_fullBright, R.drawable.popup_full_bright);
- final int fullDark = a.getResourceId(
- R.styleable.AlertDialog_fullDark, R.drawable.popup_full_dark);
+ fullBright = a.getResourceId(R.styleable.AlertDialog_fullBright, fullBright);
+ fullDark = a.getResourceId(R.styleable.AlertDialog_fullDark, fullDark);
lastView.setBackgroundResource(lastLight ? fullBright : fullDark);
}
diff --git a/core/java/com/android/internal/app/IUsageStats.aidl b/core/java/com/android/internal/app/IUsageStats.aidl
index 1ea7409..7e7f0e1 100644
--- a/core/java/com/android/internal/app/IUsageStats.aidl
+++ b/core/java/com/android/internal/app/IUsageStats.aidl
@@ -16,13 +16,17 @@
package com.android.internal.app;
+import android.app.UsageStats;
import android.content.ComponentName;
-import com.android.internal.os.PkgUsageStats;
+import android.content.res.Configuration;
+import android.os.ParcelableParcel;
interface IUsageStats {
void noteResumeComponent(in ComponentName componentName);
void notePauseComponent(in ComponentName componentName);
void noteLaunchTime(in ComponentName componentName, int millis);
- PkgUsageStats getPkgUsageStats(in ComponentName componentName);
- PkgUsageStats[] getAllPkgUsageStats();
+ void noteStartConfig(in Configuration config);
+ UsageStats.PackageStats getPkgUsageStats(String callingPkg, in ComponentName componentName);
+ UsageStats.PackageStats[] getAllPkgUsageStats(String callingPkg);
+ ParcelableParcel getCurrentStats(String callingPkg);
}
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 882bec9..41f3337 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -1108,13 +1108,6 @@ public final class ProcessStats implements Parcelable {
mRuntime = runtime;
}
}
- String webview = WebViewFactory.useExperimentalWebView() ? "chromeview" : "webview";
- if (!Objects.equals(webview, mWebView)) {
- changed = true;
- if (update) {
- mWebView = webview;
- }
- }
return changed;
}
diff --git a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java b/core/java/com/android/internal/notification/DemoContactNotificationScorer.java
deleted file mode 100644
index f484724..0000000
--- a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java
+++ /dev/null
@@ -1,188 +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.internal.notification;
-
-import android.app.Notification;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.provider.Settings;
-import android.text.SpannableString;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This NotificationScorer bumps up the priority of notifications that contain references to the
- * display names of starred contacts. The references it picks up are spannable strings which, in
- * their entirety, match the display name of some starred contact. The magnitude of the bump ranges
- * from 0 to 15 (assuming NOTIFICATION_PRIORITY_MULTIPLIER = 10) depending on the initial score, and
- * the mapping is defined by priorityBumpMap. In a production version of this scorer, a notification
- * extra will be used to specify contact identifiers.
- */
-
-public class DemoContactNotificationScorer implements NotificationScorer {
- private static final String TAG = "DemoContactNotificationScorer";
- private static final boolean DBG = false;
-
- protected static final boolean ENABLE_CONTACT_SCORER = true;
- private static final String SETTING_ENABLE_SCORER = "contact_scorer_enabled";
- protected boolean mEnabled;
-
- // see NotificationManagerService
- private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
-
- private Context mContext;
-
- private static final List<String> RELEVANT_KEYS_LIST = Arrays.asList(
- Notification.EXTRA_INFO_TEXT, Notification.EXTRA_TEXT, Notification.EXTRA_TEXT_LINES,
- Notification.EXTRA_SUB_TEXT, Notification.EXTRA_TITLE
- );
-
- private static final String[] PROJECTION = new String[] {
- ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME
- };
-
- private static final Uri CONTACTS_URI = ContactsContract.Contacts.CONTENT_URI;
-
- private static List<String> extractSpannedStrings(CharSequence charSequence) {
- if (charSequence == null) return Collections.emptyList();
- if (!(charSequence instanceof SpannableString)) {
- return Arrays.asList(charSequence.toString());
- }
- SpannableString spannableString = (SpannableString)charSequence;
- // get all spans
- Object[] ssArr = spannableString.getSpans(0, spannableString.length(), Object.class);
- // spanned string sequences
- ArrayList<String> sss = new ArrayList<String>();
- for (Object spanObj : ssArr) {
- try {
- sss.add(spannableString.subSequence(spannableString.getSpanStart(spanObj),
- spannableString.getSpanEnd(spanObj)).toString());
- } catch(StringIndexOutOfBoundsException e) {
- Slog.e(TAG, "Bad indices when extracting spanned subsequence", e);
- }
- }
- return sss;
- };
-
- private static String getQuestionMarksInParens(int n) {
- StringBuilder sb = new StringBuilder("(");
- for (int i = 0; i < n; i++) {
- if (sb.length() > 1) sb.append(',');
- sb.append('?');
- }
- sb.append(")");
- return sb.toString();
- }
-
- private boolean hasStarredContact(Bundle extras) {
- if (extras == null) return false;
- ArrayList<String> qStrings = new ArrayList<String>();
- // build list to query against the database for display names.
- for (String rk: RELEVANT_KEYS_LIST) {
- if (extras.get(rk) == null) {
- continue;
- } else if (extras.get(rk) instanceof CharSequence) {
- qStrings.addAll(extractSpannedStrings((CharSequence) extras.get(rk)));
- } else if (extras.get(rk) instanceof CharSequence[]) {
- // this is intended for Notification.EXTRA_TEXT_LINES
- for (CharSequence line: (CharSequence[]) extras.get(rk)){
- qStrings.addAll(extractSpannedStrings(line));
- }
- } else {
- Slog.w(TAG, "Strange, the extra " + rk + " is of unexpected type.");
- }
- }
- if (qStrings.isEmpty()) return false;
- String[] qStringsArr = qStrings.toArray(new String[qStrings.size()]);
-
- String selection = ContactsContract.Contacts.DISPLAY_NAME + " IN "
- + getQuestionMarksInParens(qStringsArr.length) + " AND "
- + ContactsContract.Contacts.STARRED+" ='1'";
-
- Cursor c = null;
- try {
- c = mContext.getContentResolver().query(
- CONTACTS_URI, PROJECTION, selection, qStringsArr, null);
- if (c != null) return c.getCount() > 0;
- } catch(Throwable t) {
- Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return false;
- }
-
- private final static int clamp(int x, int low, int high) {
- return (x < low) ? low : ((x > high) ? high : x);
- }
-
- private static int priorityBumpMap(int incomingScore) {
- //assumption is that scale runs from [-2*pm, 2*pm]
- int pm = NOTIFICATION_PRIORITY_MULTIPLIER;
- int theScore = incomingScore;
- // enforce input in range
- theScore = clamp(theScore, -2 * pm, 2 * pm);
- if (theScore != incomingScore) return incomingScore;
- // map -20 -> -20 and -10 -> 5 (when pm = 10)
- if (theScore <= -pm) {
- theScore += 1.5 * (theScore + 2 * pm);
- } else {
- // map 0 -> 10, 10 -> 15, 20 -> 20;
- theScore += 0.5 * (2 * pm - theScore);
- }
- if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore
- + ", score after " + theScore + ".");
- return theScore;
- }
-
- @Override
- public void initialize(Context context) {
- if (DBG) Slog.v(TAG, "Initializing " + getClass().getSimpleName() + ".");
- mContext = context;
- mEnabled = ENABLE_CONTACT_SCORER && 1 == Settings.Global.getInt(
- mContext.getContentResolver(), SETTING_ENABLE_SCORER, 0);
- }
-
- @Override
- public int getScore(Notification notification, int score) {
- if (notification == null || !mEnabled) {
- if (DBG) Slog.w(TAG, "empty notification? scorer disabled?");
- return score;
- }
- boolean hasStarredPriority = hasStarredContact(notification.extras);
-
- if (DBG) {
- if (hasStarredPriority) {
- Slog.v(TAG, "Notification references starred contact. Promoted!");
- } else {
- Slog.v(TAG, "Notification lacks any starred contact reference. Not promoted!");
- }
- }
- if (hasStarredPriority) score = priorityBumpMap(score);
- return score;
- }
-}
-
diff --git a/core/java/com/android/internal/notification/NotificationScorer.java b/core/java/com/android/internal/notification/NotificationScorer.java
deleted file mode 100644
index 863c08c..0000000
--- a/core/java/com/android/internal/notification/NotificationScorer.java
+++ /dev/null
@@ -1,27 +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.internal.notification;
-
-import android.app.Notification;
-import android.content.Context;
-
-public interface NotificationScorer {
-
- public void initialize(Context context);
- public int getScore(Notification notification, int score);
-
-}
diff --git a/core/java/com/android/internal/notification/PeopleNotificationScorer.java b/core/java/com/android/internal/notification/PeopleNotificationScorer.java
deleted file mode 100644
index efb5f63..0000000
--- a/core/java/com/android/internal/notification/PeopleNotificationScorer.java
+++ /dev/null
@@ -1,227 +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.internal.notification;
-
-import android.app.Notification;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.LruCache;
-import android.util.Slog;
-
-/**
- * This {@link NotificationScorer} attempts to validate people references.
- * Also elevates the priority of real people.
- */
-public class PeopleNotificationScorer implements NotificationScorer {
- private static final String TAG = "PeopleNotificationScorer";
- private static final boolean DBG = false;
-
- private static final boolean ENABLE_PEOPLE_SCORER = true;
- private static final String SETTING_ENABLE_PEOPLE_SCORER = "people_scorer_enabled";
- private static final String[] LOOKUP_PROJECTION = { Contacts._ID };
- private static final int MAX_PEOPLE = 10;
- private static final int PEOPLE_CACHE_SIZE = 200;
- // see NotificationManagerService
- private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
-
- protected boolean mEnabled;
- private Context mContext;
-
- // maps raw person handle to resolved person object
- private LruCache<String, LookupResult> mPeopleCache;
-
- private float findMaxContactScore(Bundle extras) {
- if (extras == null) {
- return 0f;
- }
-
- final String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE);
- if (people == null || people.length == 0) {
- return 0f;
- }
-
- float rank = 0f;
- for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
- final String handle = people[personIdx];
- if (TextUtils.isEmpty(handle)) continue;
-
- LookupResult lookupResult = mPeopleCache.get(handle);
- if (lookupResult == null || lookupResult.isExpired()) {
- final Uri uri = Uri.parse(handle);
- if ("tel".equals(uri.getScheme())) {
- if (DBG) Slog.w(TAG, "checking telephone URI: " + handle);
- lookupResult = lookupPhoneContact(handle, uri.getSchemeSpecificPart());
- } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
- if (DBG) Slog.w(TAG, "checking lookup URI: " + handle);
- lookupResult = resolveContactsUri(handle, uri);
- } else {
- if (DBG) Slog.w(TAG, "unsupported URI " + handle);
- }
- } else {
- if (DBG) Slog.w(TAG, "using cached lookupResult: " + lookupResult.mId);
- }
- if (lookupResult != null) {
- rank = Math.max(rank, lookupResult.getRank());
- }
- }
- return rank;
- }
-
- private LookupResult lookupPhoneContact(final String handle, final String number) {
- LookupResult lookupResult = null;
- Cursor c = null;
- try {
- Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
- Uri.encode(number));
- c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null);
- if (c != null && c.getCount() > 0) {
- c.moveToFirst();
- final int idIdx = c.getColumnIndex(Contacts._ID);
- final int id = c.getInt(idIdx);
- if (DBG) Slog.w(TAG, "is valid: " + id);
- lookupResult = new LookupResult(id);
- }
- } catch(Throwable t) {
- Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- if (lookupResult == null) {
- lookupResult = new LookupResult(LookupResult.INVALID_ID);
- }
- mPeopleCache.put(handle, lookupResult);
- return lookupResult;
- }
-
- private LookupResult resolveContactsUri(String handle, final Uri personUri) {
- LookupResult lookupResult = null;
- Cursor c = null;
- try {
- c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null);
- if (c != null && c.getCount() > 0) {
- c.moveToFirst();
- final int idIdx = c.getColumnIndex(Contacts._ID);
- final int id = c.getInt(idIdx);
- if (DBG) Slog.w(TAG, "is valid: " + id);
- lookupResult = new LookupResult(id);
- }
- } catch(Throwable t) {
- Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- if (lookupResult == null) {
- lookupResult = new LookupResult(LookupResult.INVALID_ID);
- }
- mPeopleCache.put(handle, lookupResult);
- return lookupResult;
- }
-
- private final static int clamp(int x, int low, int high) {
- return (x < low) ? low : ((x > high) ? high : x);
- }
-
- // TODO: rework this function before shipping
- private static int priorityBumpMap(int incomingScore) {
- //assumption is that scale runs from [-2*pm, 2*pm]
- int pm = NOTIFICATION_PRIORITY_MULTIPLIER;
- int theScore = incomingScore;
- // enforce input in range
- theScore = clamp(theScore, -2 * pm, 2 * pm);
- if (theScore != incomingScore) return incomingScore;
- // map -20 -> -20 and -10 -> 5 (when pm = 10)
- if (theScore <= -pm) {
- theScore += 1.5 * (theScore + 2 * pm);
- } else {
- // map 0 -> 10, 10 -> 15, 20 -> 20;
- theScore += 0.5 * (2 * pm - theScore);
- }
- if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore
- + ", score after " + theScore + ".");
- return theScore;
- }
-
- @Override
- public void initialize(Context context) {
- if (DBG) Slog.v(TAG, "Initializing " + getClass().getSimpleName() + ".");
- mContext = context;
- mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
- mEnabled = ENABLE_PEOPLE_SCORER && 1 == Settings.Global.getInt(
- mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_SCORER, 0);
- }
-
- @Override
- public int getScore(Notification notification, int score) {
- if (notification == null || !mEnabled) {
- if (DBG) Slog.w(TAG, "empty notification? scorer disabled?");
- return score;
- }
- float contactScore = findMaxContactScore(notification.extras);
- if (contactScore > 0f) {
- if (DBG) Slog.v(TAG, "Notification references a real contact. Promoted!");
- score = priorityBumpMap(score);
- } else {
- if (DBG) Slog.v(TAG, "Notification lacks any valid contact reference. Not promoted!");
- }
- return score;
- }
-
- private static class LookupResult {
- private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
- public static final int INVALID_ID = -1;
-
- private final long mExpireMillis;
- private int mId;
-
- public LookupResult(int id) {
- mId = id;
- mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
- }
-
- public boolean isExpired() {
- return mExpireMillis < System.currentTimeMillis();
- }
-
- public boolean isInvalid() {
- return mId == INVALID_ID || isExpired();
- }
-
- public float getRank() {
- if (isInvalid()) {
- return 0f;
- } else {
- return 1f; // TODO: finer grained score
- }
- }
-
- public LookupResult setId(int id) {
- mId = id;
- return this;
- }
- }
-}
-
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 1aff190..7bd5b12 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -188,8 +188,7 @@ public final class BatteryStatsImpl extends BatteryStats {
boolean mShuttingDown;
- HashMap<String, SparseBooleanArray>[] mActiveEvents
- = (HashMap<String, SparseBooleanArray>[]) new HashMap[HistoryItem.EVENT_COUNT];
+ final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
long mHistoryBaseTime;
boolean mHaveBatteryLevel = false;
@@ -2297,44 +2296,8 @@ public final class BatteryStatsImpl extends BatteryStats {
public void noteEventLocked(int code, String name, int uid) {
uid = mapUid(uid);
- if ((code&HistoryItem.EVENT_FLAG_START) != 0) {
- int idx = code&~HistoryItem.EVENT_FLAG_START;
- HashMap<String, SparseBooleanArray> active = mActiveEvents[idx];
- if (active == null) {
- active = new HashMap<String, SparseBooleanArray>();
- mActiveEvents[idx] = active;
- }
- SparseBooleanArray uids = active.get(name);
- if (uids == null) {
- uids = new SparseBooleanArray();
- active.put(name, uids);
- }
- if (uids.get(uid)) {
- // Already set, nothing to do!
- return;
- }
- uids.put(uid, true);
- } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) {
- int idx = code&~HistoryItem.EVENT_FLAG_FINISH;
- HashMap<String, SparseBooleanArray> active = mActiveEvents[idx];
- if (active == null) {
- // not currently active, nothing to do.
- return;
- }
- SparseBooleanArray uids = active.get(name);
- if (uids == null) {
- // not currently active, nothing to do.
- return;
- }
- idx = uids.indexOfKey(uid);
- if (idx < 0 || !uids.valueAt(idx)) {
- // not currently active, nothing to do.
- return;
- }
- uids.removeAt(idx);
- if (uids.size() <= 0) {
- active.remove(name);
- }
+ if (!mActiveEvents.updateState(code, name, uid, 0)) {
+ return;
}
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
@@ -2348,6 +2311,9 @@ public final class BatteryStatsImpl extends BatteryStats {
}
}
+ private String mInitialAcquireWakeName;
+ private int mInitialAcquireWakeUid = -1;
+
public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type,
boolean unimportantForLogging, long elapsedRealtime, long uptime) {
uid = mapUid(uid);
@@ -2360,8 +2326,9 @@ public final class BatteryStatsImpl extends BatteryStats {
if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
+ Integer.toHexString(mHistoryCur.states));
mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
- mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
- mHistoryCur.wakelockTag.uid = uid;
+ mHistoryCur.wakelockTag.string = mInitialAcquireWakeName
+ = historyName != null ? historyName : name;
+ mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
mWakeLockImportant = !unimportantForLogging;
addHistoryRecordLocked(elapsedRealtime, uptime);
} else if (!mWakeLockImportant && !unimportantForLogging) {
@@ -2369,8 +2336,9 @@ public final class BatteryStatsImpl extends BatteryStats {
// We'll try to update the last tag.
mHistoryLastWritten.wakelockTag = null;
mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
- mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
- mHistoryCur.wakelockTag.uid = uid;
+ mHistoryCur.wakelockTag.string = mInitialAcquireWakeName
+ = historyName != null ? historyName : name;
+ mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
addHistoryRecordLocked(elapsedRealtime, uptime);
}
mWakeLockImportant = true;
@@ -2395,6 +2363,14 @@ public final class BatteryStatsImpl extends BatteryStats {
mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
+ Integer.toHexString(mHistoryCur.states));
+ if ((name != null && !name.equals(mInitialAcquireWakeName))
+ || uid != mInitialAcquireWakeUid) {
+ mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
+ mHistoryCur.wakelockTag.string = name;
+ mHistoryCur.wakelockTag.uid = uid;
+ }
+ mInitialAcquireWakeName = null;
+ mInitialAcquireWakeUid = -1;
addHistoryRecordLocked(elapsedRealtime, uptime);
}
}
@@ -5699,7 +5675,8 @@ public final class BatteryStatsImpl extends BatteryStats {
final long lastRealtime = out.time;
final long lastWalltime = out.currentTime;
readHistoryDelta(mHistoryBuffer, out);
- if (out.cmd != HistoryItem.CMD_CURRENT_TIME && lastWalltime != 0) {
+ if (out.cmd != HistoryItem.CMD_CURRENT_TIME
+ && out.cmd != HistoryItem.CMD_RESET && lastWalltime != 0) {
out.currentTime = lastWalltime + (out.time - lastRealtime);
}
return true;
@@ -5845,17 +5822,15 @@ public final class BatteryStatsImpl extends BatteryStats {
private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
- HashMap<String, SparseBooleanArray> active = mActiveEvents[i];
+ HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(i);
if (active == null) {
continue;
}
- for (HashMap.Entry<String, SparseBooleanArray> ent : active.entrySet()) {
- SparseBooleanArray uids = ent.getValue();
+ for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
+ SparseIntArray uids = ent.getValue();
for (int j=0; j<uids.size(); j++) {
- if (uids.valueAt(j)) {
- addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
- uids.keyAt(j));
- }
+ addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
+ uids.keyAt(j));
}
}
}
@@ -5971,7 +5946,8 @@ public final class BatteryStatsImpl extends BatteryStats {
boolean reset) {
mRecordingHistory = true;
mHistoryCur.currentTime = System.currentTimeMillis();
- addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
+ addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs,
+ reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
mHistoryCur);
mHistoryCur.currentTime = 0;
if (reset) {
diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java
deleted file mode 100644
index 8c2c405..0000000
--- a/core/java/com/android/internal/os/PkgUsageStats.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.os;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * implementation of PkgUsageStats associated with an
- * application package.
- * @hide
- */
-public class PkgUsageStats implements Parcelable {
- public String packageName;
- public int launchCount;
- public long usageTime;
- public Map<String, Long> componentResumeTimes;
-
- public static final Parcelable.Creator<PkgUsageStats> CREATOR
- = new Parcelable.Creator<PkgUsageStats>() {
- public PkgUsageStats createFromParcel(Parcel in) {
- return new PkgUsageStats(in);
- }
-
- public PkgUsageStats[] newArray(int size) {
- return new PkgUsageStats[size];
- }
- };
-
- public String toString() {
- return "PkgUsageStats{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + packageName + "}";
- }
-
- public PkgUsageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) {
- packageName = pkgName;
- launchCount = count;
- usageTime = time;
- componentResumeTimes = new HashMap<String, Long>(lastResumeTimes);
- }
-
- public PkgUsageStats(Parcel source) {
- packageName = source.readString();
- launchCount = source.readInt();
- usageTime = source.readLong();
- final int N = source.readInt();
- componentResumeTimes = new HashMap<String, Long>(N);
- for (int i = 0; i < N; i++) {
- String component = source.readString();
- long lastResumeTime = source.readLong();
- componentResumeTimes.put(component, lastResumeTime);
- }
- }
-
- public PkgUsageStats(PkgUsageStats pStats) {
- packageName = pStats.packageName;
- launchCount = pStats.launchCount;
- usageTime = pStats.usageTime;
- componentResumeTimes = new HashMap<String, Long>(pStats.componentResumeTimes);
- }
-
- public int describeContents() {
- return 0;
- }
-
- public void writeToParcel(Parcel dest, int parcelableFlags) {
- dest.writeString(packageName);
- dest.writeInt(launchCount);
- dest.writeLong(usageTime);
- dest.writeInt(componentResumeTimes.size());
- for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) {
- dest.writeString(ent.getKey());
- dest.writeLong(ent.getValue());
- }
- }
-}
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 52281d9..34f62ba 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -450,6 +450,7 @@ public class AsyncChannel {
public void disconnect() {
if ((mConnection != null) && (mSrcContext != null)) {
mSrcContext.unbindService(mConnection);
+ mConnection = null;
}
try {
// Send the DISCONNECTED, although it may not be received
@@ -463,10 +464,12 @@ public class AsyncChannel {
// Tell source we're disconnected.
if (mSrcHandler != null) {
replyDisconnected(STATUS_SUCCESSFUL);
+ mSrcHandler = null;
}
// Unlink only when bindService isn't used
if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
+ mDeathMonitor = null;
}
}
diff --git a/core/java/com/android/internal/util/VirtualRefBasePtr.java b/core/java/com/android/internal/util/VirtualRefBasePtr.java
new file mode 100644
index 0000000..0bd4d3a
--- /dev/null
+++ b/core/java/com/android/internal/util/VirtualRefBasePtr.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util;
+
+/**
+ * Helper class that contains a strong reference to a VirtualRefBase native
+ * object. This will incStrong in the ctor, and decStrong in the finalizer
+ */
+public final class VirtualRefBasePtr {
+ private long mNativePtr;
+
+ public VirtualRefBasePtr(long ptr) {
+ mNativePtr = ptr;
+ nIncStrong(mNativePtr);
+ }
+
+ public long get() {
+ return mNativePtr;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ nDecStrong(mNativePtr);
+ mNativePtr = 0;
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private static native void nIncStrong(long ptr);
+ private static native void nDecStrong(long ptr);
+}
diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl
index 90210ce..367b713 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -21,6 +21,7 @@ import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.ExtractedText;
/**
@@ -47,4 +48,6 @@ oneway interface IInputMethodSession {
void toggleSoftInput(int showFlags, int hideFlags);
void finishSession();
+
+ void updateCursorAnchorInfo(in CursorAnchorInfo cursorAnchorInfo);
}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 51e2871..26f77b5 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -155,7 +155,8 @@ LOCAL_SRC_FILES:= \
android_content_res_Configuration.cpp \
android_animation_PropertyValuesHolder.cpp \
com_android_internal_net_NetworkStatsFactory.cpp \
- com_android_internal_os_Zygote.cpp
+ com_android_internal_os_Zygote.cpp \
+ com_android_internal_util_VirtualRefBasePtr.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 66fbb8e..9941cd9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -181,6 +181,7 @@ extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env);
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
+extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -1262,6 +1263,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_os_MemoryFile),
REG_JNI(register_com_android_internal_os_ZygoteInit),
REG_JNI(register_com_android_internal_os_Zygote),
+ REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
REG_JNI(register_android_hardware_Camera),
REG_JNI(register_android_hardware_camera2_CameraMetadata),
REG_JNI(register_android_hardware_SensorManager),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index e0fa9ba..0328517 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1,838 +1,838 @@
-#include "SkBitmap.h"
-#include "SkPixelRef.h"
-#include "SkImageEncoder.h"
-#include "SkColorPriv.h"
-#include "GraphicsJNI.h"
-#include "SkDither.h"
-#include "SkUnPreMultiply.h"
-#include "SkStream.h"
-
-#include <binder/Parcel.h>
-#include "android_os_Parcel.h"
-#include "android_util_Binder.h"
-#include "android_nio_utils.h"
-#include "CreateJavaOutputStreamAdaptor.h"
-
-#include <jni.h>
-
-#include <Caches.h>
-
-#if 0
- #define TRACE_BITMAP(code) code
-#else
- #define TRACE_BITMAP(code)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-// Conversions to/from SkColor, for get/setPixels, and the create method, which
-// is basically like setPixels
-
-typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
- int x, int y);
-
-static void FromColor_D32(void* dst, const SkColor src[], int width,
- int, int) {
- SkPMColor* d = (SkPMColor*)dst;
-
- for (int i = 0; i < width; i++) {
- *d++ = SkPreMultiplyColor(*src++);
- }
-}
-
-static void FromColor_D32_Raw(void* dst, const SkColor src[], int width,
- int, int) {
- // SkColor's ordering may be different from SkPMColor
- if (SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER) {
- memcpy(dst, src, width * sizeof(SkColor));
- return;
- }
-
- // order isn't same, repack each pixel manually
- SkPMColor* d = (SkPMColor*)dst;
- for (int i = 0; i < width; i++) {
- SkColor c = *src++;
- *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
- SkColorGetG(c), SkColorGetB(c));
- }
-}
-
-static void FromColor_D565(void* dst, const SkColor src[], int width,
- int x, int y) {
- uint16_t* d = (uint16_t*)dst;
-
- DITHER_565_SCAN(y);
- for (int stop = x + width; x < stop; x++) {
- SkColor c = *src++;
- *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),
- DITHER_VALUE(x));
- }
-}
-
-static void FromColor_D4444(void* dst, const SkColor src[], int width,
- int x, int y) {
- SkPMColor16* d = (SkPMColor16*)dst;
-
- DITHER_4444_SCAN(y);
- for (int stop = x + width; x < stop; x++) {
- SkPMColor pmc = SkPreMultiplyColor(*src++);
- *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));
-// *d++ = SkPixel32ToPixel4444(pmc);
- }
-}
-
-static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width,
- int x, int y) {
- SkPMColor16* d = (SkPMColor16*)dst;
-
- DITHER_4444_SCAN(y);
- for (int stop = x + width; x < stop; x++) {
- SkColor c = *src++;
-
- // SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied
- SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
- SkColorGetG(c), SkColorGetB(c));
- *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));
-// *d++ = SkPixel32ToPixel4444(pmc);
- }
-}
-
-// can return NULL
-static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {
- switch (config) {
- case SkBitmap::kARGB_8888_Config:
- return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw;
- case SkBitmap::kARGB_4444_Config:
- return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw;
- case SkBitmap::kRGB_565_Config:
- return FromColor_D565;
- default:
- break;
- }
- return NULL;
-}
-
-bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,
- int x, int y, int width, int height,
- const SkBitmap& dstBitmap, bool isPremultiplied) {
- SkAutoLockPixels alp(dstBitmap);
- void* dst = dstBitmap.getPixels();
- FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied);
-
- if (NULL == dst || NULL == proc) {
- return false;
- }
-
- const jint* array = env->GetIntArrayElements(srcColors, NULL);
- const SkColor* src = (const SkColor*)array + srcOffset;
-
- // reset to to actual choice from caller
- dst = dstBitmap.getAddr(x, y);
- // now copy/convert each scanline
- for (int y = 0; y < height; y++) {
- proc(dst, src, width, x, y);
- src += srcStride;
- dst = (char*)dst + dstBitmap.rowBytes();
- }
-
- dstBitmap.notifyPixelsChanged();
-
- env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
- JNI_ABORT);
- return true;
-}
-
-//////////////////// ToColor procs
-
-typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
- SkColorTable*);
-
-static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const SkPMColor* s = (const SkPMColor*)src;
- do {
- *dst++ = SkUnPreMultiply::PMColorToColor(*s++);
- } while (--width != 0);
-}
-
-static void ToColor_S32_Raw(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const SkPMColor* s = (const SkPMColor*)src;
- do {
- SkPMColor c = *s++;
- *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
- SkGetPackedG32(c), SkGetPackedB32(c));
- } while (--width != 0);
-}
-
-static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const SkPMColor* s = (const SkPMColor*)src;
- do {
- SkPMColor c = *s++;
- *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
- SkGetPackedB32(c));
- } while (--width != 0);
-}
-
-static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const SkPMColor16* s = (const SkPMColor16*)src;
- do {
- *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));
- } while (--width != 0);
-}
-
-static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const SkPMColor16* s = (const SkPMColor16*)src;
- do {
- SkPMColor c = SkPixel4444ToPixel32(*s++);
- *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
- SkGetPackedG32(c), SkGetPackedB32(c));
- } while (--width != 0);
-}
-
-static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const SkPMColor16* s = (const SkPMColor16*)src;
- do {
- SkPMColor c = SkPixel4444ToPixel32(*s++);
- *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
- SkGetPackedB32(c));
- } while (--width != 0);
-}
-
-static void ToColor_S565(SkColor dst[], const void* src, int width,
- SkColorTable*) {
- SkASSERT(width > 0);
- const uint16_t* s = (const uint16_t*)src;
- do {
- uint16_t c = *s++;
- *dst++ = SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),
- SkPacked16ToB32(c));
- } while (--width != 0);
-}
-
-static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,
- SkColorTable* ctable) {
- SkASSERT(width > 0);
- const uint8_t* s = (const uint8_t*)src;
- const SkPMColor* colors = ctable->lockColors();
- do {
- *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);
- } while (--width != 0);
- ctable->unlockColors();
-}
-
-static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width,
- SkColorTable* ctable) {
- SkASSERT(width > 0);
- const uint8_t* s = (const uint8_t*)src;
- const SkPMColor* colors = ctable->lockColors();
- do {
- SkPMColor c = colors[*s++];
- *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
- SkGetPackedG32(c), SkGetPackedB32(c));
- } while (--width != 0);
- ctable->unlockColors();
-}
-
-static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,
- SkColorTable* ctable) {
- SkASSERT(width > 0);
- const uint8_t* s = (const uint8_t*)src;
- const SkPMColor* colors = ctable->lockColors();
- do {
- SkPMColor c = colors[*s++];
- *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
- SkGetPackedB32(c));
- } while (--width != 0);
- ctable->unlockColors();
-}
-
-// can return NULL
-static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {
- switch (src.config()) {
- case SkBitmap::kARGB_8888_Config:
- if (src.isOpaque()) return ToColor_S32_Opaque;
- return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;
- case SkBitmap::kARGB_4444_Config:
- if (src.isOpaque()) return ToColor_S4444_Opaque;
- return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;
- case SkBitmap::kRGB_565_Config:
- return ToColor_S565;
- case SkBitmap::kIndex8_Config:
- if (src.getColorTable() == NULL) {
- return NULL;
- }
- if (src.isOpaque()) return ToColor_SI8_Opaque;
- return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;
- default:
- break;
- }
- return NULL;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-static int getPremulBitmapCreateFlags(bool isMutable) {
- int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;
- if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
- return flags;
-}
-
-static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
- jint offset, jint stride, jint width, jint height,
- jint configHandle, jboolean isMutable) {
- SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
- if (NULL != jColors) {
- size_t n = env->GetArrayLength(jColors);
- if (n < SkAbs32(stride) * (size_t)height) {
- doThrowAIOOBE(env);
- return NULL;
- }
- }
-
- // ARGB_4444 is a deprecated format, convert automatically to 8888
- if (config == SkBitmap::kARGB_4444_Config) {
- config = SkBitmap::kARGB_8888_Config;
- }
-
- SkBitmap bitmap;
- bitmap.setConfig(config, width, height);
-
- jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
- if (NULL == buff) {
- return NULL;
- }
-
- if (jColors != NULL) {
- GraphicsJNI::SetPixels(env, jColors, offset, stride,
- 0, 0, width, height, bitmap, true);
- }
-
- return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,
- getPremulBitmapCreateFlags(isMutable), NULL, NULL);
-}
-
-static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
- jint dstConfigHandle, jboolean isMutable) {
- const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
- SkBitmap::Config dstConfig = static_cast<SkBitmap::Config>(dstConfigHandle);
- SkBitmap result;
- JavaPixelAllocator allocator(env);
-
+#include "SkBitmap.h"
+#include "SkPixelRef.h"
+#include "SkImageEncoder.h"
+#include "SkColorPriv.h"
+#include "GraphicsJNI.h"
+#include "SkDither.h"
+#include "SkUnPreMultiply.h"
+#include "SkStream.h"
+
+#include <binder/Parcel.h>
+#include "android_os_Parcel.h"
+#include "android_util_Binder.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include <jni.h>
+
+#include <Caches.h>
+
+#if 0
+ #define TRACE_BITMAP(code) code
+#else
+ #define TRACE_BITMAP(code)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Conversions to/from SkColor, for get/setPixels, and the create method, which
+// is basically like setPixels
+
+typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
+ int x, int y);
+
+static void FromColor_D32(void* dst, const SkColor src[], int width,
+ int, int) {
+ SkPMColor* d = (SkPMColor*)dst;
+
+ for (int i = 0; i < width; i++) {
+ *d++ = SkPreMultiplyColor(*src++);
+ }
+}
+
+static void FromColor_D32_Raw(void* dst, const SkColor src[], int width,
+ int, int) {
+ // SkColor's ordering may be different from SkPMColor
+ if (SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER) {
+ memcpy(dst, src, width * sizeof(SkColor));
+ return;
+ }
+
+ // order isn't same, repack each pixel manually
+ SkPMColor* d = (SkPMColor*)dst;
+ for (int i = 0; i < width; i++) {
+ SkColor c = *src++;
+ *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
+ SkColorGetG(c), SkColorGetB(c));
+ }
+}
+
+static void FromColor_D565(void* dst, const SkColor src[], int width,
+ int x, int y) {
+ uint16_t* d = (uint16_t*)dst;
+
+ DITHER_565_SCAN(y);
+ for (int stop = x + width; x < stop; x++) {
+ SkColor c = *src++;
+ *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),
+ DITHER_VALUE(x));
+ }
+}
+
+static void FromColor_D4444(void* dst, const SkColor src[], int width,
+ int x, int y) {
+ SkPMColor16* d = (SkPMColor16*)dst;
+
+ DITHER_4444_SCAN(y);
+ for (int stop = x + width; x < stop; x++) {
+ SkPMColor pmc = SkPreMultiplyColor(*src++);
+ *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));
+// *d++ = SkPixel32ToPixel4444(pmc);
+ }
+}
+
+static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width,
+ int x, int y) {
+ SkPMColor16* d = (SkPMColor16*)dst;
+
+ DITHER_4444_SCAN(y);
+ for (int stop = x + width; x < stop; x++) {
+ SkColor c = *src++;
+
+ // SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied
+ SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
+ SkColorGetG(c), SkColorGetB(c));
+ *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));
+// *d++ = SkPixel32ToPixel4444(pmc);
+ }
+}
+
+// can return NULL
+static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {
+ switch (config) {
+ case SkBitmap::kARGB_8888_Config:
+ return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw;
+ case SkBitmap::kARGB_4444_Config:
+ return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw;
+ case SkBitmap::kRGB_565_Config:
+ return FromColor_D565;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,
+ int x, int y, int width, int height,
+ const SkBitmap& dstBitmap, bool isPremultiplied) {
+ SkAutoLockPixels alp(dstBitmap);
+ void* dst = dstBitmap.getPixels();
+ FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied);
+
+ if (NULL == dst || NULL == proc) {
+ return false;
+ }
+
+ const jint* array = env->GetIntArrayElements(srcColors, NULL);
+ const SkColor* src = (const SkColor*)array + srcOffset;
+
+ // reset to to actual choice from caller
+ dst = dstBitmap.getAddr(x, y);
+ // now copy/convert each scanline
+ for (int y = 0; y < height; y++) {
+ proc(dst, src, width, x, y);
+ src += srcStride;
+ dst = (char*)dst + dstBitmap.rowBytes();
+ }
+
+ dstBitmap.notifyPixelsChanged();
+
+ env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
+ JNI_ABORT);
+ return true;
+}
+
+//////////////////// ToColor procs
+
+typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
+ SkColorTable*);
+
+static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
+ SkColorTable*) {
+ SkASSERT(width > 0);
+ const SkPMColor* s = (const SkPMColor*)src;
+ do {
+ *dst++ = SkUnPreMultiply::PMColorToColor(*s++);
+ } while (--width != 0);
+}
+
+static void ToColor_S32_Raw(SkColor dst[], const void* src, int width,
+ SkColorTable*) {
+ SkASSERT(width > 0);
+ const SkPMColor* s = (const SkPMColor*)src;
+ do {
+ SkPMColor c = *s++;
+ *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
+ SkGetPackedG32(c), SkGetPackedB32(c));
+ } while (--width != 0);
+}
+
+static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,
+ SkColorTable*) {
+ SkASSERT(width > 0);
+ const SkPMColor* s = (const SkPMColor*)src;
+ do {
+ SkPMColor c = *s++;
+ *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
+ SkGetPackedB32(c));
+ } while (--width != 0);
+}
+
+static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,
+ SkColorTable*) {
+ SkASSERT(width > 0);
+ const SkPMColor16* s = (const SkPMColor16*)src;
+ do {
+ *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));
+ } while (--width != 0);
+}
+
+static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width,
+ SkColorTable*) {
+ SkASSERT(width > 0);
+ const SkPMColor16* s = (const SkPMColor16*)src;
+ do {
+ SkPMColor c = SkPixel4444ToPixel32(*s++);
+ *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
+ SkGetPackedG32(c), SkGetPackedB32(c));
+ } while (--width != 0);
+}
+
+static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,
+ SkColorTable*) {
+ SkASSERT(width > 0);
+ const SkPMColor16* s = (const SkPMColor16*)src;
+ do {
+ SkPMColor c = SkPixel4444ToPixel32(*s++);
+ *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
+ SkGetPackedB32(c));
+ } while (--width != 0);
+}
+
+static void ToColor_S565(SkColor dst[], const void* src, int width,
+ SkColorTable*) {
+ SkASSERT(width > 0);
+ const uint16_t* s = (const uint16_t*)src;
+ do {
+ uint16_t c = *s++;
+ *dst++ = SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),
+ SkPacked16ToB32(c));
+ } while (--width != 0);
+}
+
+static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,
+ SkColorTable* ctable) {
+ SkASSERT(width > 0);
+ const uint8_t* s = (const uint8_t*)src;
+ const SkPMColor* colors = ctable->lockColors();
+ do {
+ *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);
+ } while (--width != 0);
+ ctable->unlockColors();
+}
+
+static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width,
+ SkColorTable* ctable) {
+ SkASSERT(width > 0);
+ const uint8_t* s = (const uint8_t*)src;
+ const SkPMColor* colors = ctable->lockColors();
+ do {
+ SkPMColor c = colors[*s++];
+ *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
+ SkGetPackedG32(c), SkGetPackedB32(c));
+ } while (--width != 0);
+ ctable->unlockColors();
+}
+
+static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,
+ SkColorTable* ctable) {
+ SkASSERT(width > 0);
+ const uint8_t* s = (const uint8_t*)src;
+ const SkPMColor* colors = ctable->lockColors();
+ do {
+ SkPMColor c = colors[*s++];
+ *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
+ SkGetPackedB32(c));
+ } while (--width != 0);
+ ctable->unlockColors();
+}
+
+// can return NULL
+static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {
+ switch (src.config()) {
+ case SkBitmap::kARGB_8888_Config:
+ if (src.isOpaque()) return ToColor_S32_Opaque;
+ return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;
+ case SkBitmap::kARGB_4444_Config:
+ if (src.isOpaque()) return ToColor_S4444_Opaque;
+ return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;
+ case SkBitmap::kRGB_565_Config:
+ return ToColor_S565;
+ case SkBitmap::kIndex8_Config:
+ if (src.getColorTable() == NULL) {
+ return NULL;
+ }
+ if (src.isOpaque()) return ToColor_SI8_Opaque;
+ return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int getPremulBitmapCreateFlags(bool isMutable) {
+ int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;
+ if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
+ return flags;
+}
+
+static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
+ jint offset, jint stride, jint width, jint height,
+ jint configHandle, jboolean isMutable) {
+ SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
+ if (NULL != jColors) {
+ size_t n = env->GetArrayLength(jColors);
+ if (n < SkAbs32(stride) * (size_t)height) {
+ doThrowAIOOBE(env);
+ return NULL;
+ }
+ }
+
+ // ARGB_4444 is a deprecated format, convert automatically to 8888
+ if (config == SkBitmap::kARGB_4444_Config) {
+ config = SkBitmap::kARGB_8888_Config;
+ }
+
+ SkBitmap bitmap;
+ bitmap.setConfig(config, width, height);
+
+ jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
+ if (NULL == buff) {
+ return NULL;
+ }
+
+ if (jColors != NULL) {
+ GraphicsJNI::SetPixels(env, jColors, offset, stride,
+ 0, 0, width, height, bitmap, true);
+ }
+
+ return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,
+ getPremulBitmapCreateFlags(isMutable), NULL, NULL);
+}
+
+static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
+ jint dstConfigHandle, jboolean isMutable) {
+ const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
+ SkBitmap::Config dstConfig = static_cast<SkBitmap::Config>(dstConfigHandle);
+ SkBitmap result;
+ JavaPixelAllocator allocator(env);
+
if (!src->copyTo(&result, SkBitmapConfigToColorType(dstConfig), &allocator)) {
- return NULL;
- }
- return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),
- getPremulBitmapCreateFlags(isMutable), NULL, NULL);
-}
-
-static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-#ifdef USE_OPENGL_RENDERER
- if (android::uirenderer::Caches::hasInstance()) {
- android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);
- return;
- }
-#endif // USE_OPENGL_RENDERER
- delete bitmap;
-}
-
-static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-#ifdef USE_OPENGL_RENDERER
- if (android::uirenderer::Caches::hasInstance()) {
- bool result;
- result = android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);
- return result ? JNI_TRUE : JNI_FALSE;
- }
-#endif // USE_OPENGL_RENDERER
- bitmap->setPixels(NULL, NULL);
- return JNI_TRUE;
-}
-
-static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
- jint width, jint height, jint configHandle, jint allocSize) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
- if (width * height * SkBitmap::ComputeBytesPerPixel(config) > 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);
- 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.
- ref->notifyPixelsChanged();
- SkSafeUnref(ref);
-}
-
-// These must match the int values in Bitmap.java
-enum JavaEncodeFormat {
- kJPEG_JavaEncodeFormat = 0,
- kPNG_JavaEncodeFormat = 1,
- kWEBP_JavaEncodeFormat = 2
-};
-
-static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
- jint format, jint quality,
- jobject jstream, jbyteArray jstorage) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkImageEncoder::Type fm;
-
- switch (format) {
- case kJPEG_JavaEncodeFormat:
- fm = SkImageEncoder::kJPEG_Type;
- break;
- case kPNG_JavaEncodeFormat:
- fm = SkImageEncoder::kPNG_Type;
- break;
- case kWEBP_JavaEncodeFormat:
- fm = SkImageEncoder::kWEBP_Type;
- break;
- default:
- return JNI_FALSE;
- }
-
- bool success = false;
- if (NULL != bitmap) {
- SkAutoLockPixels alp(*bitmap);
-
- if (NULL == bitmap->getPixels()) {
- return JNI_FALSE;
- }
-
- SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
- if (NULL == strm) {
- return JNI_FALSE;
- }
-
- SkImageEncoder* encoder = SkImageEncoder::Create(fm);
- if (NULL != encoder) {
- success = encoder->encodeStream(strm, *bitmap, quality);
- delete encoder;
- }
- delete strm;
- }
- return success ? JNI_TRUE : JNI_FALSE;
-}
-
-static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- bitmap->eraseColor(color);
-}
-
-static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- return static_cast<jint>(bitmap->rowBytes());
-}
-
-static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- return static_cast<jint>(bitmap->config());
-}
-
-static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- return static_cast<jint>(bitmap->getGenerationID());
-}
-
-static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE;
-}
-
-static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
- jboolean hasAlpha, jboolean isPremul) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- if (!hasAlpha) {
- bitmap->setAlphaType(kOpaque_SkAlphaType);
- } else if (isPremul) {
- bitmap->setAlphaType(kPremul_SkAlphaType);
- } else {
- bitmap->setAlphaType(kUnpremul_SkAlphaType);
- }
-}
-
-static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE;
-}
-
-static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle,
- jboolean hasMipMap) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- bitmap->setHasHardwareMipMap(hasMipMap);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
- if (parcel == NULL) {
- SkDebugf("-------- unparcel parcel is NULL\n");
- return NULL;
- }
-
- android::Parcel* p = android::parcelForJavaObject(env, parcel);
-
- const bool isMutable = p->readInt32() != 0;
- const SkBitmap::Config config = (SkBitmap::Config)p->readInt32();
- const int width = p->readInt32();
- const int height = p->readInt32();
- const int rowBytes = p->readInt32();
- const int density = p->readInt32();
-
- if (SkBitmap::kARGB_8888_Config != config &&
- SkBitmap::kRGB_565_Config != config &&
- SkBitmap::kARGB_4444_Config != config &&
- SkBitmap::kIndex8_Config != config &&
- SkBitmap::kA8_Config != config) {
- SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
- return NULL;
- }
-
- SkBitmap* bitmap = new SkBitmap;
-
- bitmap->setConfig(config, width, height, rowBytes);
-
- SkColorTable* ctable = NULL;
- if (config == SkBitmap::kIndex8_Config) {
- int count = p->readInt32();
- if (count > 0) {
- size_t size = count * sizeof(SkPMColor);
- const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
- ctable = new SkColorTable(src, count);
- }
- }
-
- jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
- if (NULL == buffer) {
- SkSafeUnref(ctable);
- delete bitmap;
- return NULL;
- }
-
- SkSafeUnref(ctable);
-
- size_t size = bitmap->getSize();
-
- android::Parcel::ReadableBlob blob;
- android::status_t status = p->readBlob(size, &blob);
- if (status) {
- doThrowRE(env, "Could not read bitmap from parcel blob.");
- delete bitmap;
- return NULL;
- }
-
- bitmap->lockPixels();
- memcpy(bitmap->getPixels(), blob.data(), size);
- bitmap->unlockPixels();
-
- blob.release();
-
- return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),
- NULL, NULL, density);
-}
-
-static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
- jlong bitmapHandle,
- jboolean isMutable, jint density,
- jobject parcel) {
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- if (parcel == NULL) {
- SkDebugf("------- writeToParcel null parcel\n");
- return JNI_FALSE;
- }
-
- android::Parcel* p = android::parcelForJavaObject(env, parcel);
-
- p->writeInt32(isMutable);
- p->writeInt32(bitmap->config());
- p->writeInt32(bitmap->width());
- p->writeInt32(bitmap->height());
- p->writeInt32(bitmap->rowBytes());
- p->writeInt32(density);
-
- if (bitmap->config() == SkBitmap::kIndex8_Config) {
- SkColorTable* ctable = bitmap->getColorTable();
- if (ctable != NULL) {
- int count = ctable->count();
- p->writeInt32(count);
- memcpy(p->writeInplace(count * sizeof(SkPMColor)),
- ctable->lockColors(), count * sizeof(SkPMColor));
- ctable->unlockColors();
- } else {
- p->writeInt32(0); // indicate no ctable
- }
- }
-
- size_t size = bitmap->getSize();
-
- android::Parcel::WritableBlob blob;
- android::status_t status = p->writeBlob(size, &blob);
- if (status) {
- doThrowRE(env, "Could not write bitmap to parcel blob.");
- return JNI_FALSE;
- }
-
- bitmap->lockPixels();
- const void* pSrc = bitmap->getPixels();
- if (pSrc == NULL) {
- memset(blob.data(), 0, size);
- } else {
- memcpy(blob.data(), pSrc, size);
- }
- bitmap->unlockPixels();
-
- blob.release();
- return JNI_TRUE;
-}
-
-static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
- jlong srcHandle, jlong paintHandle,
- jintArray offsetXY) {
- const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
- const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
- SkIPoint offset;
- SkBitmap* dst = new SkBitmap;
- JavaPixelAllocator allocator(env);
-
- src->extractAlpha(dst, paint, &allocator, &offset);
- // If Skia can't allocate pixels for destination bitmap, it resets
- // it, that is set its pixels buffer to NULL, and zero width and height.
- if (dst->getPixels() == NULL && src->getPixels() != NULL) {
- delete dst;
- doThrowOOME(env, "failed to allocate pixels for alpha");
- return NULL;
- }
- if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
- int* array = env->GetIntArrayElements(offsetXY, NULL);
- array[0] = offset.fX;
- array[1] = offset.fY;
- env->ReleaseIntArrayElements(offsetXY, array, 0);
- }
-
- return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),
- getPremulBitmapCreateFlags(true), NULL, NULL);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
- jint x, jint y, jboolean isPremultiplied) {
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkAutoLockPixels alp(*bitmap);
-
- ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
- if (NULL == proc) {
- return 0;
- }
- const void* src = bitmap->getAddr(x, y);
- if (NULL == src) {
- return 0;
- }
-
- SkColor dst[1];
- proc(dst, src, 1, bitmap->getColorTable());
- return static_cast<jint>(dst[0]);
-}
-
-static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
- jintArray pixelArray, jint offset, jint stride,
- jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkAutoLockPixels alp(*bitmap);
-
- ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
- if (NULL == proc) {
- return;
- }
- const void* src = bitmap->getAddr(x, y);
- if (NULL == src) {
- return;
- }
-
- SkColorTable* ctable = bitmap->getColorTable();
- jint* dst = env->GetIntArrayElements(pixelArray, NULL);
- SkColor* d = (SkColor*)dst + offset;
- while (--height >= 0) {
- proc(d, src, width, ctable);
- d += stride;
- src = (void*)((const char*)src + bitmap->rowBytes());
- }
- env->ReleaseIntArrayElements(pixelArray, dst, 0);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
- jint x, jint y, jint colorHandle, jboolean isPremultiplied) {
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkColor color = static_cast<SkColor>(colorHandle);
- SkAutoLockPixels alp(*bitmap);
- if (NULL == bitmap->getPixels()) {
- return;
- }
-
- FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);
- if (NULL == proc) {
- return;
- }
-
- proc(bitmap->getAddr(x, y), &color, 1, x, y);
- bitmap->notifyPixelsChanged();
-}
-
-static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,
- jintArray pixelArray, jint offset, jint stride,
- jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
- x, y, width, height, *bitmap, isPremultiplied);
-}
-
-static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
- jlong bitmapHandle, jobject jbuffer) {
- const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkAutoLockPixels alp(*bitmap);
- const void* src = bitmap->getPixels();
-
- if (NULL != src) {
- android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
-
- // the java side has already checked that buffer is large enough
- memcpy(abp.pointer(), src, bitmap->getSize());
- }
-}
-
-static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
- jlong bitmapHandle, jobject jbuffer) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- SkAutoLockPixels alp(*bitmap);
- void* dst = bitmap->getPixels();
-
- if (NULL != dst) {
- android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
- // the java side has already checked that buffer is large enough
- memcpy(dst, abp.pointer(), bitmap->getSize());
- bitmap->notifyPixelsChanged();
- }
-}
-
-static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
- jlong bm1Handle) {
- const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle);
- const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle);
- if (bm0->width() != bm1->width() ||
- bm0->height() != bm1->height() ||
- bm0->config() != bm1->config()) {
- return JNI_FALSE;
- }
-
- SkAutoLockPixels alp0(*bm0);
- SkAutoLockPixels alp1(*bm1);
-
- // if we can't load the pixels, return false
- if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
- return JNI_FALSE;
- }
-
- if (bm0->config() == SkBitmap::kIndex8_Config) {
- SkColorTable* ct0 = bm0->getColorTable();
- SkColorTable* ct1 = bm1->getColorTable();
- if (NULL == ct0 || NULL == ct1) {
- return JNI_FALSE;
- }
- if (ct0->count() != ct1->count()) {
- return JNI_FALSE;
- }
-
- SkAutoLockColors alc0(ct0);
- SkAutoLockColors alc1(ct1);
- const size_t size = ct0->count() * sizeof(SkPMColor);
- if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
- return JNI_FALSE;
- }
- }
-
- // now compare each scanline. We can't do the entire buffer at once,
- // since we don't care about the pixel values that might extend beyond
- // the width (since the scanline might be larger than the logical width)
- const int h = bm0->height();
- const size_t size = bm0->width() * bm0->bytesPerPixel();
- for (int y = 0; y < h; y++) {
- if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
- return JNI_FALSE;
- }
- }
- return JNI_TRUE;
-}
-
-static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) {
- SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
- bitmap->lockPixels();
- bitmap->unlockPixels();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include <android_runtime/AndroidRuntime.h>
-
-static JNINativeMethod gBitmapMethods[] = {
- { "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;",
- (void*)Bitmap_creator },
- { "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;",
- (void*)Bitmap_copy },
- { "nativeDestructor", "(J)V", (void*)Bitmap_destructor },
- { "nativeRecycle", "(J)Z", (void*)Bitmap_recycle },
- { "nativeReconfigure", "(JIIII)V", (void*)Bitmap_reconfigure },
- { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z",
- (void*)Bitmap_compress },
- { "nativeErase", "(JI)V", (void*)Bitmap_erase },
- { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes },
- { "nativeConfig", "(J)I", (void*)Bitmap_config },
- { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha },
- { "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied},
- { "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap },
- { "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap },
- { "nativeCreateFromParcel",
- "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
- (void*)Bitmap_createFromParcel },
- { "nativeWriteToParcel", "(JZILandroid/os/Parcel;)Z",
- (void*)Bitmap_writeToParcel },
- { "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;",
- (void*)Bitmap_extractAlpha },
- { "nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId },
- { "nativeGetPixel", "(JIIZ)I", (void*)Bitmap_getPixel },
- { "nativeGetPixels", "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels },
- { "nativeSetPixel", "(JIIIZ)V", (void*)Bitmap_setPixel },
- { "nativeSetPixels", "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels },
- { "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
- (void*)Bitmap_copyPixelsToBuffer },
- { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
- (void*)Bitmap_copyPixelsFromBuffer },
- { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs },
- { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw },
-};
-
-#define kClassPathName "android/graphics/Bitmap"
-
-int register_android_graphics_Bitmap(JNIEnv* env)
-{
- return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
- gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
-}
+ return NULL;
+ }
+ return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),
+ getPremulBitmapCreateFlags(isMutable), NULL, NULL);
+}
+
+static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+#ifdef USE_OPENGL_RENDERER
+ if (android::uirenderer::Caches::hasInstance()) {
+ android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);
+ return;
+ }
+#endif // USE_OPENGL_RENDERER
+ delete bitmap;
+}
+
+static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+#ifdef USE_OPENGL_RENDERER
+ if (android::uirenderer::Caches::hasInstance()) {
+ bool result;
+ result = android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);
+ return result ? JNI_TRUE : JNI_FALSE;
+ }
+#endif // USE_OPENGL_RENDERER
+ bitmap->setPixels(NULL, NULL);
+ return JNI_TRUE;
+}
+
+static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
+ jint width, jint height, jint configHandle, jint allocSize) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
+ if (width * height * SkBitmap::ComputeBytesPerPixel(config) > 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);
+ 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.
+ ref->notifyPixelsChanged();
+ SkSafeUnref(ref);
+}
+
+// These must match the int values in Bitmap.java
+enum JavaEncodeFormat {
+ kJPEG_JavaEncodeFormat = 0,
+ kPNG_JavaEncodeFormat = 1,
+ kWEBP_JavaEncodeFormat = 2
+};
+
+static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
+ jint format, jint quality,
+ jobject jstream, jbyteArray jstorage) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkImageEncoder::Type fm;
+
+ switch (format) {
+ case kJPEG_JavaEncodeFormat:
+ fm = SkImageEncoder::kJPEG_Type;
+ break;
+ case kPNG_JavaEncodeFormat:
+ fm = SkImageEncoder::kPNG_Type;
+ break;
+ case kWEBP_JavaEncodeFormat:
+ fm = SkImageEncoder::kWEBP_Type;
+ break;
+ default:
+ return JNI_FALSE;
+ }
+
+ bool success = false;
+ if (NULL != bitmap) {
+ SkAutoLockPixels alp(*bitmap);
+
+ if (NULL == bitmap->getPixels()) {
+ return JNI_FALSE;
+ }
+
+ SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
+ if (NULL == strm) {
+ return JNI_FALSE;
+ }
+
+ SkImageEncoder* encoder = SkImageEncoder::Create(fm);
+ if (NULL != encoder) {
+ success = encoder->encodeStream(strm, *bitmap, quality);
+ delete encoder;
+ }
+ delete strm;
+ }
+ return success ? JNI_TRUE : JNI_FALSE;
+}
+
+static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ bitmap->eraseColor(color);
+}
+
+static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ return static_cast<jint>(bitmap->rowBytes());
+}
+
+static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ return static_cast<jint>(bitmap->config());
+}
+
+static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ return static_cast<jint>(bitmap->getGenerationID());
+}
+
+static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE;
+}
+
+static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
+ jboolean hasAlpha, jboolean isPremul) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ if (!hasAlpha) {
+ bitmap->setAlphaType(kOpaque_SkAlphaType);
+ } else if (isPremul) {
+ bitmap->setAlphaType(kPremul_SkAlphaType);
+ } else {
+ bitmap->setAlphaType(kUnpremul_SkAlphaType);
+ }
+}
+
+static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE;
+}
+
+static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle,
+ jboolean hasMipMap) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ bitmap->setHasHardwareMipMap(hasMipMap);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
+ if (parcel == NULL) {
+ SkDebugf("-------- unparcel parcel is NULL\n");
+ return NULL;
+ }
+
+ android::Parcel* p = android::parcelForJavaObject(env, parcel);
+
+ const bool isMutable = p->readInt32() != 0;
+ const SkBitmap::Config config = (SkBitmap::Config)p->readInt32();
+ const int width = p->readInt32();
+ const int height = p->readInt32();
+ const int rowBytes = p->readInt32();
+ const int density = p->readInt32();
+
+ if (SkBitmap::kARGB_8888_Config != config &&
+ SkBitmap::kRGB_565_Config != config &&
+ SkBitmap::kARGB_4444_Config != config &&
+ SkBitmap::kIndex8_Config != config &&
+ SkBitmap::kA8_Config != config) {
+ SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
+ return NULL;
+ }
+
+ SkBitmap* bitmap = new SkBitmap;
+
+ bitmap->setConfig(config, width, height, rowBytes);
+
+ SkColorTable* ctable = NULL;
+ if (config == SkBitmap::kIndex8_Config) {
+ int count = p->readInt32();
+ if (count > 0) {
+ size_t size = count * sizeof(SkPMColor);
+ const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
+ ctable = new SkColorTable(src, count);
+ }
+ }
+
+ jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
+ if (NULL == buffer) {
+ SkSafeUnref(ctable);
+ delete bitmap;
+ return NULL;
+ }
+
+ SkSafeUnref(ctable);
+
+ size_t size = bitmap->getSize();
+
+ android::Parcel::ReadableBlob blob;
+ android::status_t status = p->readBlob(size, &blob);
+ if (status) {
+ doThrowRE(env, "Could not read bitmap from parcel blob.");
+ delete bitmap;
+ return NULL;
+ }
+
+ bitmap->lockPixels();
+ memcpy(bitmap->getPixels(), blob.data(), size);
+ bitmap->unlockPixels();
+
+ blob.release();
+
+ return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),
+ NULL, NULL, density);
+}
+
+static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
+ jlong bitmapHandle,
+ jboolean isMutable, jint density,
+ jobject parcel) {
+ const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ if (parcel == NULL) {
+ SkDebugf("------- writeToParcel null parcel\n");
+ return JNI_FALSE;
+ }
+
+ android::Parcel* p = android::parcelForJavaObject(env, parcel);
+
+ p->writeInt32(isMutable);
+ p->writeInt32(bitmap->config());
+ p->writeInt32(bitmap->width());
+ p->writeInt32(bitmap->height());
+ p->writeInt32(bitmap->rowBytes());
+ p->writeInt32(density);
+
+ if (bitmap->config() == SkBitmap::kIndex8_Config) {
+ SkColorTable* ctable = bitmap->getColorTable();
+ if (ctable != NULL) {
+ int count = ctable->count();
+ p->writeInt32(count);
+ memcpy(p->writeInplace(count * sizeof(SkPMColor)),
+ ctable->lockColors(), count * sizeof(SkPMColor));
+ ctable->unlockColors();
+ } else {
+ p->writeInt32(0); // indicate no ctable
+ }
+ }
+
+ size_t size = bitmap->getSize();
+
+ android::Parcel::WritableBlob blob;
+ android::status_t status = p->writeBlob(size, &blob);
+ if (status) {
+ doThrowRE(env, "Could not write bitmap to parcel blob.");
+ return JNI_FALSE;
+ }
+
+ bitmap->lockPixels();
+ const void* pSrc = bitmap->getPixels();
+ if (pSrc == NULL) {
+ memset(blob.data(), 0, size);
+ } else {
+ memcpy(blob.data(), pSrc, size);
+ }
+ bitmap->unlockPixels();
+
+ blob.release();
+ return JNI_TRUE;
+}
+
+static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
+ jlong srcHandle, jlong paintHandle,
+ jintArray offsetXY) {
+ const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
+ const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+ SkIPoint offset;
+ SkBitmap* dst = new SkBitmap;
+ JavaPixelAllocator allocator(env);
+
+ src->extractAlpha(dst, paint, &allocator, &offset);
+ // If Skia can't allocate pixels for destination bitmap, it resets
+ // it, that is set its pixels buffer to NULL, and zero width and height.
+ if (dst->getPixels() == NULL && src->getPixels() != NULL) {
+ delete dst;
+ doThrowOOME(env, "failed to allocate pixels for alpha");
+ return NULL;
+ }
+ if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
+ int* array = env->GetIntArrayElements(offsetXY, NULL);
+ array[0] = offset.fX;
+ array[1] = offset.fY;
+ env->ReleaseIntArrayElements(offsetXY, array, 0);
+ }
+
+ return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),
+ getPremulBitmapCreateFlags(true), NULL, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
+ jint x, jint y, jboolean isPremultiplied) {
+ const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkAutoLockPixels alp(*bitmap);
+
+ ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
+ if (NULL == proc) {
+ return 0;
+ }
+ const void* src = bitmap->getAddr(x, y);
+ if (NULL == src) {
+ return 0;
+ }
+
+ SkColor dst[1];
+ proc(dst, src, 1, bitmap->getColorTable());
+ return static_cast<jint>(dst[0]);
+}
+
+static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
+ jintArray pixelArray, jint offset, jint stride,
+ jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
+ const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkAutoLockPixels alp(*bitmap);
+
+ ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
+ if (NULL == proc) {
+ return;
+ }
+ const void* src = bitmap->getAddr(x, y);
+ if (NULL == src) {
+ return;
+ }
+
+ SkColorTable* ctable = bitmap->getColorTable();
+ jint* dst = env->GetIntArrayElements(pixelArray, NULL);
+ SkColor* d = (SkColor*)dst + offset;
+ while (--height >= 0) {
+ proc(d, src, width, ctable);
+ d += stride;
+ src = (void*)((const char*)src + bitmap->rowBytes());
+ }
+ env->ReleaseIntArrayElements(pixelArray, dst, 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
+ jint x, jint y, jint colorHandle, jboolean isPremultiplied) {
+ const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkColor color = static_cast<SkColor>(colorHandle);
+ SkAutoLockPixels alp(*bitmap);
+ if (NULL == bitmap->getPixels()) {
+ return;
+ }
+
+ FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);
+ if (NULL == proc) {
+ return;
+ }
+
+ proc(bitmap->getAddr(x, y), &color, 1, x, y);
+ bitmap->notifyPixelsChanged();
+}
+
+static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,
+ jintArray pixelArray, jint offset, jint stride,
+ jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
+ const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
+ x, y, width, height, *bitmap, isPremultiplied);
+}
+
+static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
+ jlong bitmapHandle, jobject jbuffer) {
+ const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkAutoLockPixels alp(*bitmap);
+ const void* src = bitmap->getPixels();
+
+ if (NULL != src) {
+ android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
+
+ // the java side has already checked that buffer is large enough
+ memcpy(abp.pointer(), src, bitmap->getSize());
+ }
+}
+
+static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
+ jlong bitmapHandle, jobject jbuffer) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ SkAutoLockPixels alp(*bitmap);
+ void* dst = bitmap->getPixels();
+
+ if (NULL != dst) {
+ android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
+ // the java side has already checked that buffer is large enough
+ memcpy(dst, abp.pointer(), bitmap->getSize());
+ bitmap->notifyPixelsChanged();
+ }
+}
+
+static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
+ jlong bm1Handle) {
+ const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle);
+ const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle);
+ if (bm0->width() != bm1->width() ||
+ bm0->height() != bm1->height() ||
+ bm0->config() != bm1->config()) {
+ return JNI_FALSE;
+ }
+
+ SkAutoLockPixels alp0(*bm0);
+ SkAutoLockPixels alp1(*bm1);
+
+ // if we can't load the pixels, return false
+ if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
+ return JNI_FALSE;
+ }
+
+ if (bm0->config() == SkBitmap::kIndex8_Config) {
+ SkColorTable* ct0 = bm0->getColorTable();
+ SkColorTable* ct1 = bm1->getColorTable();
+ if (NULL == ct0 || NULL == ct1) {
+ return JNI_FALSE;
+ }
+ if (ct0->count() != ct1->count()) {
+ return JNI_FALSE;
+ }
+
+ SkAutoLockColors alc0(ct0);
+ SkAutoLockColors alc1(ct1);
+ const size_t size = ct0->count() * sizeof(SkPMColor);
+ if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
+ return JNI_FALSE;
+ }
+ }
+
+ // now compare each scanline. We can't do the entire buffer at once,
+ // since we don't care about the pixel values that might extend beyond
+ // the width (since the scanline might be larger than the logical width)
+ const int h = bm0->height();
+ const size_t size = bm0->width() * bm0->bytesPerPixel();
+ for (int y = 0; y < h; y++) {
+ if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
+ return JNI_FALSE;
+ }
+ }
+ return JNI_TRUE;
+}
+
+static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) {
+ SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+ bitmap->lockPixels();
+ bitmap->unlockPixels();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gBitmapMethods[] = {
+ { "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_creator },
+ { "nativeCopy", "(JIZ)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_copy },
+ { "nativeDestructor", "(J)V", (void*)Bitmap_destructor },
+ { "nativeRecycle", "(J)Z", (void*)Bitmap_recycle },
+ { "nativeReconfigure", "(JIIII)V", (void*)Bitmap_reconfigure },
+ { "nativeCompress", "(JIILjava/io/OutputStream;[B)Z",
+ (void*)Bitmap_compress },
+ { "nativeErase", "(JI)V", (void*)Bitmap_erase },
+ { "nativeRowBytes", "(J)I", (void*)Bitmap_rowBytes },
+ { "nativeConfig", "(J)I", (void*)Bitmap_config },
+ { "nativeHasAlpha", "(J)Z", (void*)Bitmap_hasAlpha },
+ { "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied},
+ { "nativeHasMipMap", "(J)Z", (void*)Bitmap_hasMipMap },
+ { "nativeSetHasMipMap", "(JZ)V", (void*)Bitmap_setHasMipMap },
+ { "nativeCreateFromParcel",
+ "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_createFromParcel },
+ { "nativeWriteToParcel", "(JZILandroid/os/Parcel;)Z",
+ (void*)Bitmap_writeToParcel },
+ { "nativeExtractAlpha", "(JJ[I)Landroid/graphics/Bitmap;",
+ (void*)Bitmap_extractAlpha },
+ { "nativeGenerationId", "(J)I", (void*)Bitmap_getGenerationId },
+ { "nativeGetPixel", "(JIIZ)I", (void*)Bitmap_getPixel },
+ { "nativeGetPixels", "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels },
+ { "nativeSetPixel", "(JIIIZ)V", (void*)Bitmap_setPixel },
+ { "nativeSetPixels", "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels },
+ { "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
+ (void*)Bitmap_copyPixelsToBuffer },
+ { "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
+ (void*)Bitmap_copyPixelsFromBuffer },
+ { "nativeSameAs", "(JJ)Z", (void*)Bitmap_sameAs },
+ { "nativePrepareToDraw", "(J)V", (void*)Bitmap_prepareToDraw },
+};
+
+#define kClassPathName "android/graphics/Bitmap"
+
+int register_android_graphics_Bitmap(JNIEnv* env)
+{
+ return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+ gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
+}
diff --git a/core/jni/android/graphics/CanvasProperty.cpp b/core/jni/android/graphics/CanvasProperty.cpp
index 70e2db5..cfa9cd8 100644
--- a/core/jni/android/graphics/CanvasProperty.cpp
+++ b/core/jni/android/graphics/CanvasProperty.cpp
@@ -18,7 +18,7 @@
#include "GraphicsJNI.h"
#include <android_runtime/AndroidRuntime.h>
-#include <utils/VirtualLightRefBase.h>
+#include <utils/RefBase.h>
#include <CanvasProperty.h>
namespace android {
@@ -27,22 +27,13 @@ using namespace uirenderer;
#ifdef USE_OPENGL_RENDERER
-static jlong incRef(VirtualLightRefBase* ptr) {
- ptr->incStrong(0);
- return reinterpret_cast<jlong>(ptr);
-}
-
static jlong createFloat(JNIEnv* env, jobject clazz, jfloat initialValue) {
- return incRef(new CanvasPropertyPrimitive(initialValue));
+ return reinterpret_cast<jlong>(new CanvasPropertyPrimitive(initialValue));
}
static jlong createPaint(JNIEnv* env, jobject clazz, jlong paintPtr) {
const SkPaint* paint = reinterpret_cast<const SkPaint*>(paintPtr);
- return incRef(new CanvasPropertyPaint(*paint));
-}
-
-static void unref(JNIEnv* env, jobject clazz, jlong containerPtr) {
- reinterpret_cast<VirtualLightRefBase*>(containerPtr)->decStrong(0);
+ return reinterpret_cast<jlong>(new CanvasPropertyPaint(*paint));
}
#endif
@@ -57,7 +48,6 @@ static JNINativeMethod gMethods[] = {
#ifdef USE_OPENGL_RENDERER
{ "nCreateFloat", "(F)J", (void*) createFloat },
{ "nCreatePaint", "(J)J", (void*) createPaint },
- { "nUnref", "(J)V", (void*) unref },
#endif
};
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index da752752..463a0a8 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -526,12 +526,15 @@ jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* da
switch (format) {
default:
- // TODO Currently the only possible values for format are AUDIO_FORMAT_PCM_16_BIT
- // and AUDIO_FORMAT_PCM_8_BIT, due to the limited set of values for audioFormat.
+ // TODO Currently the only possible values for format are AUDIO_FORMAT_PCM_16_BIT,
+ // AUDIO_FORMAT_PCM_8_BIT, and AUDIO_FORMAT_PCM_FLOAT,
+ // due to the limited set of values for audioFormat.
// The next section of the switch will probably work for more formats, but it has only
- // been tested for AUDIO_FORMAT_PCM_16_BIT, so that's why the "default" case fails.
+ // been tested for AUDIO_FORMAT_PCM_16_BIT and AUDIO_FORMAT_PCM_FLOAT,
+ // so that's why the "default" case fails.
break;
+ case AUDIO_FORMAT_PCM_FLOAT:
case AUDIO_FORMAT_PCM_16_BIT: {
// writing to shared memory, check for capacity
if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
@@ -679,6 +682,44 @@ static jint android_media_AudioTrack_write_short(JNIEnv *env, jobject thiz,
// ----------------------------------------------------------------------------
+static jint android_media_AudioTrack_write_float(JNIEnv *env, jobject thiz,
+ jfloatArray javaAudioData,
+ jint offsetInFloats, jint sizeInFloats,
+ jint javaAudioFormat,
+ jboolean isWriteBlocking) {
+
+ sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+ if (lpTrack == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Unable to retrieve AudioTrack pointer for write()");
+ return 0;
+ }
+
+ jfloat* cAudioData = NULL;
+ if (javaAudioData) {
+ cAudioData = (jfloat *)env->GetFloatArrayElements(javaAudioData, NULL);
+ if (cAudioData == NULL) {
+ ALOGE("Error retrieving source of audio data to play, can't play");
+ return 0; // out of memory or no data to load
+ }
+ } else {
+ ALOGE("NULL java array of audio data to play, can't play");
+ return 0;
+ }
+ jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData,
+ offsetInFloats * sizeof(float), sizeInFloats * sizeof(float),
+ isWriteBlocking == JNI_TRUE /* blocking */);
+ env->ReleaseFloatArrayElements(javaAudioData, cAudioData, 0);
+
+ if (written > 0) {
+ written /= sizeof(float);
+ }
+
+ return written;
+}
+
+
+// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_get_native_frame_count(JNIEnv *env, jobject thiz) {
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
if (lpTrack == NULL) {
@@ -963,6 +1004,7 @@ static JNINativeMethod gMethods[] = {
"(Ljava/lang/Object;IIIZ)I",
(void *)android_media_AudioTrack_write_native_bytes},
{"native_write_short", "([SIII)I", (void *)android_media_AudioTrack_write_short},
+ {"native_write_float", "([FIIIZ)I",(void *)android_media_AudioTrack_write_float},
{"native_setVolume", "(FF)V", (void *)android_media_AudioTrack_set_volume},
{"native_get_native_frame_count",
"()I", (void *)android_media_AudioTrack_get_native_frame_count},
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index 3be013b..4787d28 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -101,7 +101,6 @@ static jlong createAnimator(JNIEnv* env, jobject clazz, jobject weakThis,
RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw);
BaseAnimator* animator = new RenderPropertyAnimator(property, deltaType, deltaValue);
- animator->incStrong(0);
animator->setListener(new AnimationListenerBridge(env, weakThis));
return reinterpret_cast<jlong>( animator );
}
@@ -111,7 +110,6 @@ static jlong createCanvasPropertyFloatAnimator(JNIEnv* env, jobject clazz,
RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw);
CanvasPropertyPrimitive* canvasProperty = reinterpret_cast<CanvasPropertyPrimitive*>(canvasPropertyPtr);
BaseAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, deltaType, deltaValue);
- animator->incStrong(0);
animator->setListener(new AnimationListenerBridge(env, weakThis));
return reinterpret_cast<jlong>( animator );
}
@@ -124,7 +122,6 @@ static jlong createCanvasPropertyPaintAnimator(JNIEnv* env, jobject clazz,
CanvasPropertyPaintAnimator::PaintField paintField = toPaintField(paintFieldRaw);
BaseAnimator* animator = new CanvasPropertyPaintAnimator(
canvasProperty, paintField, deltaType, deltaValue);
- animator->incStrong(0);
animator->setListener(new AnimationListenerBridge(env, weakThis));
return reinterpret_cast<jlong>( animator );
}
@@ -135,11 +132,6 @@ static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jint dura
animator->setDuration(duration);
}
-static void unref(JNIEnv* env, jobject clazz, jlong objPtr) {
- VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr);
- obj->decStrong(0);
-}
-
#endif
// ----------------------------------------------------------------------------
@@ -154,7 +146,6 @@ static JNINativeMethod gMethods[] = {
{ "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyFloatAnimator },
{ "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIIF)J", (void*) createCanvasPropertyPaintAnimator },
{ "nSetDuration", "(JI)V", (void*) setDuration },
- { "nUnref", "(J)V", (void*) unref },
#endif
};
diff --git a/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp
new file mode 100644
index 0000000..ce6f207
--- /dev/null
+++ b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+namespace android {
+
+static void incStrong(JNIEnv* env, jobject clazz, jlong objPtr) {
+ VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr);
+ obj->incStrong(0);
+}
+
+static void decStrong(JNIEnv* env, jobject clazz, jlong objPtr) {
+ VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr);
+ obj->decStrong(0);
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "com/android/internal/util/VirtualRefBasePtr";
+
+static JNINativeMethod gMethods[] = {
+ { "nIncStrong", "(J)V", (void*) incStrong },
+ { "nDecStrong", "(J)V", (void*) decStrong },
+};
+
+int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv* env) {
+ return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+
+} // namespace android
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..1880a15
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..aecb4d2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..8401f91
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..5832865
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..6d14962
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..aee057c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..fb5801e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..fdb5271
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..b8c7397
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..d0395a8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..59bb437
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..c053b90
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..eb30a79
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..1af0bff
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..3b36e7d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..c12d20a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..882365b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..f6c7094
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..0e326c9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..8bf1170
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..cedb66e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..257d7ba
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..e07b36e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..ef94200
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..ad67004
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..50796e2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..ba7be9e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..bdbfe78
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..fe89951
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..840c88f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..621d1d2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..fd8be89
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.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_qntm_alpha.png
index f1023ea..5a99528 100644
--- a/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-hdpi/scrubber_control_off_qntm_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_qntm_alpha.png
index 15ceeee..79de664 100644
--- a/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
index 90b1498..73e8f1c 100644
--- a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
index b535758..ff6affe 100644
--- a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..0f44ff9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..9d5dda0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..e4ce802
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..d1806ac
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..ab9315b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..46e90e6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..e8c56ff
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..59dcb7e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..e9bd4a2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..1d05037
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..91b40de
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..c531cab
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..11bb387
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..8843210
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..6ff2f3d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..a03c1e2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..0a22e1a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..2e2469c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..c1054d9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..cf8d80a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..9d9e870
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..1bad701
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..a84a54f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..4d8050b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..374172c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..233036e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..61d9b58
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..274e983
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..acf16e5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..ee48241
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..dbbb736
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..bcabd0d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.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_qntm_alpha.png
index 1833704..e40cba8 100644
--- a/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-mdpi/scrubber_control_off_qntm_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_qntm_alpha.png
index e64d3f2..437a3e3 100644
--- a/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
index ffd6c39..8949b52 100644
--- a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
index 15faff0..d727683 100644
--- a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml
index 37df348..49028b4 100644
--- a/core/res/res/drawable-nodpi/stat_sys_adb.xml
+++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml
@@ -21,7 +21,7 @@
<group>
<path
android:name="adb"
- android:pathData="m3,3l8,0l0,11l11,0l0,8l-18,0z"
+ android:pathData="m3,3l8,0l0,11l11,0l0,8l-19,0z"
android:fill="#FFFFFFFF"
/>
</group>
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..25500e8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..b136e25
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..6a94e30
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..d386421
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..c811385
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..58b3267
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..0659e72
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..b4227d1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..714ef00
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..139595b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..4491107
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..20eb752
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..532c9f2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..0d78a32
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..af29678
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..23eb9e3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..cd11e14
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..b10db83
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..efeb6fb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..83080af
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..b9cc322
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..3b5f9c4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..58c93db
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..0f1d010
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..05a7a0f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..9345035
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..5f149b7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..191f369
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..44e08e6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..5a9dfa0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..ee921c6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..567bb0c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.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_qntm_alpha.png
index ad72f06..729e0bf 100644
--- a/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_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_qntm_alpha.png
index 7aceed1..d018a7c 100644
--- a/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
index 309b528..a7a972c 100644
--- a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
index 139795e..dd8910b 100644
--- a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..1881f54
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..6f8ec2d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..c954ed9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..9d1a47e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..ce63631
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..430c134
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..cdebf83
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..40ceadb
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..fb13eb2
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..d716fba
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..b8be041
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..bad0c3c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..a6368fb
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..234e5d1
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..3e7796d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..0673999
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.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_qntm_alpha.png
new file mode 100644
index 0000000..4779944
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00000_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..866f7b7
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00001_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..76aae57
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00002_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..0cd470a
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00003_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..0015b39
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00004_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..2f69f5b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00005_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..77142fd
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00006_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..2f81277
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00007_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..d37fe60
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00008_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..cb62079
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00009_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..82dc428
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00010_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..2cba2fb
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00011_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..5de1952
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00012_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..1c22a17
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00013_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..7f652fc
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00014_qntm_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_qntm_alpha.png
new file mode 100644
index 0000000..076acbd
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00015_qntm_alpha.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_qntm_alpha.png
index c11b0ae..a2b5716 100644
--- a/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_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_qntm_alpha.png
index cde797e..caabc2c 100644
--- a/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
index 9e234af..8d79a13 100644
--- a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
index b371eab..e0e4ef9 100644
--- a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_quantum_anim.xml b/core/res/res/drawable/btn_check_quantum_anim.xml
index 4b329ad..96715a4 100644
--- a/core/res/res/drawable/btn_check_quantum_anim.xml
+++ b/core/res/res/drawable/btn_check_quantum_anim.xml
@@ -1,3 +1,4 @@
+<?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");
@@ -13,21 +14,92 @@
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:versionCode="1" >
+<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_anim_00015_qntm_alpha"
+ android:tint="?attr/colorControlActivated"
+ android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_enabled="false">
+ <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha"
+ android:tint="?attr/colorControlNormal"
+ android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_checked="true" android:id="@+id/on">
+ <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:id="@+id/off">
+ <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <transition android:fromId="@+id/off" android:toId="@+id/on" android:reversible="true">
+ <animation-list>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00001_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00002_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00003_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00004_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00005_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00006_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00007_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00008_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00009_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00010_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00011_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00012_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00013_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00014_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+</animated-selector>
- <size
- android:height="32dp"
- android:width="32dp" />
-
- <viewport
- android:viewportHeight="320"
- android:viewportWidth="320" />
-
- <group>
- <path
- android:name="check"
- android:pathData="M 232.1,80.6 L 248.5,92.1 L 145.2,239.5 L 71.5,187.8 L 83,171.5 L 140.3,211.7 z"
- android:fill="?attr/colorControlActivated" />
- </group>
-</vector>
diff --git a/core/res/res/drawable/btn_radio_quantum_anim.xml b/core/res/res/drawable/btn_radio_quantum_anim.xml
new file mode 100644
index 0000000..5068b7a
--- /dev/null
+++ b/core/res/res/drawable/btn_radio_quantum_anim.xml
@@ -0,0 +1,104 @@
+<?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_anim_00015_qntm_alpha"
+ android:tint="?attr/colorControlActivated"
+ android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_enabled="false">
+ <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha"
+ android:tint="?attr/colorControlNormal"
+ android:alpha="?attr/disabledAlpha" />
+ </item>
+ <item android:state_checked="true" android:id="@+id/on">
+ <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:id="@+id/off">
+ <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </item>
+ <transition android:fromId="@+id/off" android:toId="@+id/on" android:reversible="true">
+ <animation-list>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00001_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00002_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00003_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00004_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00005_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00006_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00007_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00008_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00009_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00010_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00011_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00012_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00013_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00014_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ <item android:duration="33">
+ <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </item>
+ </animation-list>
+ </transition>
+</animated-selector>
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
index d172b05..f82fe7a 100644
--- a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
+++ b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
@@ -16,22 +16,26 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false">
- <bitmap android:src="@drawable/scrubber_track_qntm_alpha"
+ <nine-patch android:src="@drawable/scrubber_track_qntm_alpha"
android:tint="?attr/colorControlNormal" />
</item>
<item>
<layer-list>
<item android:id="@id/background">
- <bitmap android:src="@drawable/scrubber_track_qntm_alpha"
+ <nine-patch android:src="@drawable/scrubber_track_qntm_alpha"
android:tint="?attr/colorControlNormal" />
</item>
<item android:id="@id/secondaryProgress">
- <bitmap android:src="@drawable/scrubber_primary_qntm_alpha"
- android:tint="?attr/colorControlNormal" />
+ <scale android:scaleWidth="100%">
+ <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha"
+ android:tint="?attr/colorControlNormal" />
+ </scale>
</item>
<item android:id="@id/progress">
- <bitmap android:src="@drawable/scrubber_primary_qntm_alpha"
- android:tint="?attr/colorControlActivated" />
+ <scale android:scaleWidth="100%">
+ <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha"
+ android:tint="?attr/colorControlActivated" />
+ </scale>
</item>
</layer-list>
</item>
diff --git a/core/res/res/layout/alert_dialog_micro.xml b/core/res/res/layout/alert_dialog_micro.xml
index f8eb46c..abdbd16 100644
--- a/core/res/res/layout/alert_dialog_micro.xml
+++ b/core/res/res/layout/alert_dialog_micro.xml
@@ -20,6 +20,8 @@
android:id="@+id/parentPanel"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="@android:color/white"
+ android:layout_gravity="center"
android:orientation="vertical">
<LinearLayout android:id="@+id/topPanel"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d9473ec..cedb92d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3343,6 +3343,8 @@
<attr name="thumb" format="reference" />
<!-- An offset for the thumb that allows it to extend out of the range of the track. -->
<attr name="thumbOffset" format="dimension" />
+ <!-- Whether to split the track and leave a gap for the thumb drawable. -->
+ <attr name="splitTrack" format="boolean" />
</declare-styleable>
<declare-styleable name="StackView">
@@ -4239,6 +4241,58 @@
<attr name="autoMirrored"/>
</declare-styleable>
+ <!-- Drawable used to render several states with animated transitions. Each state
+ is represented by a child drawable with an optional keyframe ID. -->
+ <declare-styleable name="AnimatedStateListDrawable">
+ <!-- Indicates whether the drawable should be initially visible. -->
+ <attr name="visible" />
+ <!-- If true, allows the drawable's padding to change based on the
+ current state that is selected. If false, the padding will
+ stay the same (based on the maximum padding of all the states).
+ Enabling this feature requires that the owner of the drawable
+ deal with performing layout when the state changes, which is
+ often not supported. -->
+ <attr name="variablePadding" />
+ <!-- If true, the drawable's reported internal size will remain
+ constant as the state changes; the size is the maximum of all
+ of the states. If false, the size will vary based on the
+ current state. -->
+ <attr name="constantSize" />
+ <!-- Enables or disables dithering of the bitmap if the bitmap does not have the
+ same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with
+ an RGB 565 screen). -->
+ <attr name="dither" />
+ <!-- Amount of time (in milliseconds) to fade in a new state drawable. -->
+ <attr name="enterFadeDuration" />
+ <!-- Amount of time (in milliseconds) to fade out an old state drawable. -->
+ <attr name="exitFadeDuration" />
+ <!-- Indicates if the drawable needs to be mirrored when its layout direction is
+ RTL (right-to-left). -->
+ <attr name="autoMirrored"/>
+ </declare-styleable>
+
+ <!-- Transition used to animate between states with keyframe IDs. -->
+ <declare-styleable name="AnimatedStateListDrawableItem">
+ <!-- Reference to a drawable resource to use for the frame. If not
+ given, the drawable must be defined by the first child tag. -->
+ <attr name="drawable" />
+ <!-- Keyframe identifier for use in specifying transitions. -->
+ <attr name="id" />
+ </declare-styleable>
+
+ <!-- Transition used to animate between states with keyframe IDs. -->
+ <declare-styleable name="AnimatedStateListDrawableTransition">
+ <!-- Keyframe identifier for the starting state. -->
+ <attr name="fromId" format="reference" />
+ <!-- Keyframe identifier for the ending state. -->
+ <attr name="toId" format="reference" />
+ <!-- Reference to a animation drawable resource to use for the frame. If not
+ given, the animation drawable must be defined by the first child tag. -->
+ <attr name="drawable" />
+ <!-- Whether this transition is reversible. -->
+ <attr name="reversible" format="boolean" />
+ </declare-styleable>
+
<!-- Drawable used to render several animated frames. -->
<declare-styleable name="AnimationDrawable">
<attr name="visible" />
@@ -6341,6 +6395,8 @@
<attr name="switchMinWidth" format="dimension" />
<!-- Minimum space between the switch and caption text -->
<attr name="switchPadding" format="dimension" />
+ <!-- Whether to split the track and leave a gap for the thumb drawable. -->
+ <attr name="splitTrack" />
</declare-styleable>
<declare-styleable name="Pointer">
@@ -6553,4 +6609,8 @@
<attr name="layout_gravity" />
</declare-styleable>
+ <!-- Used as a filter array on the theme to pull out only the EdgeEffect-relevant bits. -->
+ <declare-styleable name="EdgeEffect">
+ <attr name="colorPrimaryLight" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f39155b..83cbb74 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1393,8 +1393,10 @@
<item>com.android.inputmethod.latin</item>
</string-array>
- <string-array name="config_notificationScorers">
- <item>com.android.internal.notification.PeopleNotificationScorer</item>
+ <!-- The list of classes that should be added to the notification ranking pipline.
+ See {@link com.android.server.notification.NotificationSignalExtractortor} -->
+ <string-array name="config_notificationSignalExtractors">
+ <item>com.android.server.notification.ValidateNotificationPeople</item>
</string-array>
<!-- Flag indicating that this device does not rotate and will always remain in its default
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6b2c788..bf92f9b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -143,6 +143,8 @@
<!-- Preferred width of the search view. -->
<dimen name="search_view_preferred_width">320dip</dimen>
+ <!-- Dialog padding for round display -->
+ <dimen name="alert_dialog_round_padding">27dip</dimen>
<!-- Dialog title height -->
<dimen name="alert_dialog_title_height">64dip</dimen>
<!-- Dialog button bar height -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4e584c0..dc5efea 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2166,6 +2166,10 @@
<public type="attr" name="documentLaunchMode" />
<public type="attr" name="autoRemoveFromRecents" />
<public type="attr" name="stateListAnimator" />
+ <public type="attr" name="toId" />
+ <public type="attr" name="fromId" />
+ <public type="attr" name="reversible" />
+ <public type="attr" name="splitTrack" />
<public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
index bdaa49d..5bac1f9 100644
--- a/core/res/res/values/styles_micro.xml
+++ b/core/res/res/values/styles_micro.xml
@@ -15,6 +15,16 @@
-->
<resources>
<style name="AlertDialog.Micro" parent="AlertDialog.Holo.Light">
+ <item name="fullDark">@null</item>
+ <item name="topDark">@null</item>
+ <item name="centerDark">@null</item>
+ <item name="bottomDark">@null</item>
+ <item name="fullBright">@null</item>
+ <item name="topBright">@null</item>
+ <item name="centerBright">@null</item>
+ <item name="bottomBright">@null</item>
+ <item name="bottomMedium">@null</item>
+ <item name="centerMedium">@null</item>
<item name="layout">@layout/alert_dialog_micro</item>
</style>
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index 88a2a9f..e693673 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -455,10 +455,10 @@ please see styles_device_defaults.xml.
<style name="Widget.Quantum.CompoundButton.Switch">
<item name="track">@drawable/switch_track_quantum</item>
<item name="thumb">@drawable/switch_inner_quantum</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="thumbTextPadding">12dip</item>
<item name="switchMinWidth">72dip</item>
<item name="switchPadding">16dip</item>
<item name="background">?attr/selectableItemBackground</item>
@@ -572,10 +572,8 @@ please see styles_device_defaults.xml.
<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="minHeight">13dip</item>
- <item name="maxHeight">13dip</item>
<item name="thumb">@drawable/scrubber_control_selector_quantum</item>
- <item name="thumbOffset">16dip</item>
+ <item name="splitTrack">true</item>
<item name="focusable">true</item>
<item name="paddingStart">16dip</item>
<item name="paddingEnd">16dip</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2bf72e8..1057cc2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -120,6 +120,7 @@
<java-symbol type="id" name="overlay_display_window_title" />
<java-symbol type="id" name="package_label" />
<java-symbol type="id" name="packages_list" />
+ <java-symbol type="id" name="parentPanel" />
<java-symbol type="id" name="pause" />
<java-symbol type="id" name="perms_list" />
<java-symbol type="id" name="perm_icon" />
@@ -325,6 +326,7 @@
<java-symbol type="color" name="tab_indicator_text_v4" />
<java-symbol type="dimen" name="accessibility_touch_slop" />
+ <java-symbol type="dimen" name="alert_dialog_round_padding"/>
<java-symbol type="dimen" name="config_prefDialogWidth" />
<java-symbol type="dimen" name="config_viewConfigurationTouchSlop" />
<java-symbol type="dimen" name="config_viewMinFlingVelocity" />
@@ -1653,7 +1655,7 @@
<java-symbol type="id" name="button_always" />
<java-symbol type="integer" name="config_globalActionsKeyTimeout" />
<java-symbol type="integer" name="config_maxResolverActivityColumns" />
- <java-symbol type="array" name="config_notificationScorers" />
+ <java-symbol type="array" name="config_notificationSignalExtractors" />
<java-symbol type="layout" name="notification_quantum_action" />
<java-symbol type="layout" name="notification_quantum_action_list" />
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
index 39df700..9647947 100644
--- a/core/res/res/values/themes_micro.xml
+++ b/core/res/res/values/themes_micro.xml
@@ -47,10 +47,15 @@
<item name="textAppearanceInverse">@style/TextAppearance.Micro</item>
</style>
- <style name="Theme.Micro.Dialog.Alert" parent="Theme.Holo.Light.Dialog.Alert">
+ <style name="Theme.Micro.Dialog.Alert">
<item name="windowTitleStyle">@style/DialogWindowTitle.Micro</item>
<item name="alertDialogStyle">@style/AlertDialog.Micro</item>
<item name="windowIsFloating">false</item>
+ <item name="windowBackground">@android:color/transparent</item>
+ <item name="windowOverscan">true</item>
+ <item name="windowContentOverlay">@null</item>
+ <item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
+ <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
</style>
<style name="Theme.Micro.Dialog.AppError" parent="Theme.Micro.Dialog">
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index 768fd9a..18d6f80 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -122,8 +122,8 @@ please see themes_device_defaults.xml.
<item name="listDivider">@drawable/list_divider_quantum</item>
<item name="listSeparatorTextViewStyle">@style/Widget.Quantum.TextView.ListSeparator</item>
- <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item>
- <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum</item>
+ <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum_anim</item>
+ <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
<item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
@@ -466,8 +466,8 @@ please see themes_device_defaults.xml.
<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</item>
- <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum</item>
+ <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum_anim</item>
+ <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
<item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
diff --git a/core/tests/inputmethodtests/run_core_inputmethod_test.sh b/core/tests/inputmethodtests/run_core_inputmethod_test.sh
index 9029ba5..e0f4f6d 100755
--- a/core/tests/inputmethodtests/run_core_inputmethod_test.sh
+++ b/core/tests/inputmethodtests/run_core_inputmethod_test.sh
@@ -21,4 +21,4 @@ if [[ $rebuild == true ]]; then
$COMMAND
fi
-adb shell am instrument -w -e class android.os.InputMethodTest,android.os.InputMethodSubtypeArrayTest,android.os.InputMethodSubtypeSwitchingControllerTest com.android.frameworks.coretests.inputmethod/android.test.InstrumentationTestRunner
+adb shell am instrument -w -e class android.os.InputMethodTest,android.os.InputMethodSubtypeArrayTest,android.os.InputMethodSubtypeSwitchingControllerTest,android.os.CursorAnchorInfoTest,android.os.SparseRectFArrayTest com.android.frameworks.coretests.inputmethod/android.test.InstrumentationTestRunner
diff --git a/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.java
new file mode 100644
index 0000000..59a6314
--- /dev/null
+++ b/core/tests/inputmethodtests/src/android/os/CursorAnchorInfoTest.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.os;
+
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder;
+
+public class CursorAnchorInfoTest extends InstrumentationTestCase {
+ // null represents a character that is invisible, for example because it's overlapped by some
+ // other UI elements.
+ private static final RectF[] MANY_RECTS = new RectF[] {
+ null,
+ new RectF(102.0f, 202.0f, 302.0f, 402.0f),
+ new RectF(103.0f, 203.0f, 303.0f, 403.0f),
+ new RectF(104.0f, 204.0f, 304.0f, 404.0f),
+ new RectF(105.0f, 205.0f, 305.0f, 405.0f),
+ new RectF(106.0f, 206.0f, 306.0f, 406.0f),
+ null,
+ new RectF(108.0f, 208.0f, 308.0f, 408.0f),
+ new RectF(109.0f, 209.0f, 309.0f, 409.0f),
+ new RectF(110.0f, 210.0f, 310.0f, 410.0f),
+ new RectF(111.0f, 211.0f, 311.0f, 411.0f),
+ new RectF(112.0f, 212.0f, 312.0f, 412.0f),
+ new RectF(113.0f, 213.0f, 313.0f, 413.0f),
+ new RectF(114.0f, 214.0f, 314.0f, 414.0f),
+ new RectF(115.0f, 215.0f, 315.0f, 415.0f),
+ new RectF(116.0f, 216.0f, 316.0f, 416.0f),
+ new RectF(117.0f, 217.0f, 317.0f, 417.0f),
+ null,
+ null,
+ };
+
+ @SmallTest
+ public void testBuilder() throws Exception {
+ final int SELECTION_START = 30;
+ final int SELECTION_END = 40;
+ final int CANDIDATES_START = 32;
+ final int CANDIDATES_END = 33;
+ final float INSERTION_MARKER_HORIZONTAL = 10.5f;
+ final float INSERTION_MARKER_TOP = 100.1f;
+ final float INSERTION_MARKER_BASELINE = 110.4f;
+ final float INSERTION_MARKER_BOTOM = 111.0f;
+ Matrix TRANSFORM_MATRIX = new Matrix(Matrix.IDENTITY_MATRIX);
+ TRANSFORM_MATRIX.setScale(10.0f, 20.0f);
+
+ final CursorAnchorInfoBuilder builder = new CursorAnchorInfoBuilder();
+ builder.setSelectionRange(SELECTION_START, SELECTION_END)
+ .setCandidateRange(CANDIDATES_START, CANDIDATES_END)
+ .setInsertionMarkerLocation(INSERTION_MARKER_HORIZONTAL, INSERTION_MARKER_TOP,
+ INSERTION_MARKER_BASELINE, INSERTION_MARKER_BOTOM)
+ .setMatrix(TRANSFORM_MATRIX);
+ for (int i = 0; i < MANY_RECTS.length; i++) {
+ final RectF rect = MANY_RECTS[i];
+ if (rect != null) {
+ builder.addCharacterRect(i, rect.left, rect.top, rect.right, rect.bottom);
+ }
+ }
+
+ final CursorAnchorInfo info = builder.build();
+ assertEquals(SELECTION_START, info.getSelectionStart());
+ assertEquals(SELECTION_END, info.getSelectionEnd());
+ assertEquals(CANDIDATES_START, info.getCandidatesStart());
+ assertEquals(CANDIDATES_END, info.getCandidatesEnd());
+ assertEquals(INSERTION_MARKER_HORIZONTAL, info.getInsertionMarkerHorizontal());
+ assertEquals(INSERTION_MARKER_TOP, info.getInsertionMarkerTop());
+ assertEquals(INSERTION_MARKER_BASELINE, info.getInsertionMarkerBaseline());
+ assertEquals(INSERTION_MARKER_BOTOM, info.getInsertionMarkerBottom());
+ assertEquals(TRANSFORM_MATRIX, info.getMatrix());
+ for (int i = 0; i < MANY_RECTS.length; i++) {
+ final RectF rect = MANY_RECTS[i];
+ assertEquals(rect, info.getCharacterRect(i));
+ }
+
+ // Make sure that the builder can reproduce the same object.
+ final CursorAnchorInfo info2 = builder.build();
+ assertEquals(SELECTION_START, info2.getSelectionStart());
+ assertEquals(SELECTION_END, info2.getSelectionEnd());
+ assertEquals(CANDIDATES_START, info2.getCandidatesStart());
+ assertEquals(CANDIDATES_END, info2.getCandidatesEnd());
+ assertEquals(INSERTION_MARKER_HORIZONTAL, info2.getInsertionMarkerHorizontal());
+ assertEquals(INSERTION_MARKER_TOP, info2.getInsertionMarkerTop());
+ assertEquals(INSERTION_MARKER_BASELINE, info2.getInsertionMarkerBaseline());
+ assertEquals(INSERTION_MARKER_BOTOM, info2.getInsertionMarkerBottom());
+ assertEquals(TRANSFORM_MATRIX, info2.getMatrix());
+ for (int i = 0; i < MANY_RECTS.length; i++) {
+ final RectF rect = MANY_RECTS[i];
+ assertEquals(rect, info2.getCharacterRect(i));
+ }
+ assertEquals(info, info2);
+ assertEquals(info.hashCode(), info2.hashCode());
+
+ // Make sure that object can be marshalled via {@link Parsel}.
+ final CursorAnchorInfo info3 = cloneViaParcel(info2);
+ assertEquals(SELECTION_START, info3.getSelectionStart());
+ assertEquals(SELECTION_END, info3.getSelectionEnd());
+ assertEquals(CANDIDATES_START, info3.getCandidatesStart());
+ assertEquals(CANDIDATES_END, info3.getCandidatesEnd());
+ assertEquals(INSERTION_MARKER_HORIZONTAL, info3.getInsertionMarkerHorizontal());
+ assertEquals(INSERTION_MARKER_TOP, info3.getInsertionMarkerTop());
+ assertEquals(INSERTION_MARKER_BASELINE, info3.getInsertionMarkerBaseline());
+ assertEquals(INSERTION_MARKER_BOTOM, info3.getInsertionMarkerBottom());
+ assertEquals(TRANSFORM_MATRIX, info3.getMatrix());
+ for (int i = 0; i < MANY_RECTS.length; i++) {
+ final RectF rect = MANY_RECTS[i];
+ assertEquals(rect, info3.getCharacterRect(i));
+ }
+ assertEquals(info.hashCode(), info3.hashCode());
+
+ builder.reset();
+ final CursorAnchorInfo uninitializedInfo = builder.build();
+ assertEquals(-1, uninitializedInfo.getSelectionStart());
+ assertEquals(-1, uninitializedInfo.getSelectionEnd());
+ assertEquals(-1, uninitializedInfo.getCandidatesStart());
+ assertEquals(-1, uninitializedInfo.getCandidatesEnd());
+ assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerHorizontal());
+ assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerTop());
+ assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBaseline());
+ assertEquals(Float.NaN, uninitializedInfo.getInsertionMarkerBottom());
+ assertEquals(Matrix.IDENTITY_MATRIX, uninitializedInfo.getMatrix());
+ }
+
+ @SmallTest
+ public void testBuilderAdd() throws Exception {
+ // A negative index should be rejected.
+ try {
+ new CursorAnchorInfoBuilder().addCharacterRect(-1, 0.0f, 0.0f, 0.0f, 0.0f);
+ } catch (IllegalArgumentException ex) {
+ assertTrue(true);
+ }
+ }
+
+ private static CursorAnchorInfo cloneViaParcel(final CursorAnchorInfo src) {
+ Parcel parcel = null;
+ try {
+ parcel = Parcel.obtain();
+ src.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return new CursorAnchorInfo(parcel);
+ } finally {
+ if (parcel != null) {
+ parcel.recycle();
+ }
+ }
+ }
+}
+
diff --git a/core/tests/inputmethodtests/src/android/os/SparseRectFArrayTest.java b/core/tests/inputmethodtests/src/android/os/SparseRectFArrayTest.java
new file mode 100644
index 0000000..fae7230
--- /dev/null
+++ b/core/tests/inputmethodtests/src/android/os/SparseRectFArrayTest.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 android.os;
+
+import android.graphics.RectF;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.inputmethod.SparseRectFArray;
+import android.view.inputmethod.SparseRectFArray.SparseRectFArrayBuilder;
+
+import java.util.Objects;
+
+public class SparseRectFArrayTest extends InstrumentationTestCase {
+ // A test data for {@link SparseRectFArray}. null represents the gap of indices.
+ private static final RectF[] MANY_RECTS = new RectF[] {
+ null,
+ new RectF(102.0f, 202.0f, 302.0f, 402.0f),
+ new RectF(103.0f, 203.0f, 303.0f, 403.0f),
+ new RectF(104.0f, 204.0f, 304.0f, 404.0f),
+ new RectF(105.0f, 205.0f, 305.0f, 405.0f),
+ new RectF(106.0f, 206.0f, 306.0f, 406.0f),
+ null,
+ new RectF(108.0f, 208.0f, 308.0f, 408.0f),
+ new RectF(109.0f, 209.0f, 309.0f, 409.0f),
+ new RectF(110.0f, 210.0f, 310.0f, 410.0f),
+ new RectF(111.0f, 211.0f, 311.0f, 411.0f),
+ new RectF(112.0f, 212.0f, 312.0f, 412.0f),
+ new RectF(113.0f, 213.0f, 313.0f, 413.0f),
+ new RectF(114.0f, 214.0f, 314.0f, 414.0f),
+ new RectF(115.0f, 215.0f, 315.0f, 415.0f),
+ new RectF(116.0f, 216.0f, 316.0f, 416.0f),
+ new RectF(117.0f, 217.0f, 317.0f, 417.0f),
+ null,
+ null,
+ new RectF(118.0f, 218.0f, 318.0f, 418.0f),
+ };
+
+ @SmallTest
+ public void testBuilder() throws Exception {
+ final RectF TEMP_RECT = new RectF(10.0f, 20.0f, 30.0f, 40.0f);
+
+ final SparseRectFArrayBuilder builder = new SparseRectFArrayBuilder();
+ builder.append(100, TEMP_RECT.left, TEMP_RECT.top, TEMP_RECT.right, TEMP_RECT.bottom);
+ assertNull(builder.build().get(-1));
+ assertNull(builder.build().get(0));
+ assertNull(builder.build().get(99));
+ assertEquals(TEMP_RECT, builder.build().get(100));
+ assertNull(builder.build().get(101));
+
+ // Test if {@link SparseRectFArrayBuilder#reset} resets its internal state.
+ builder.reset();
+ assertNull(builder.build().get(100));
+
+ builder.reset();
+ for (int i = 0; i < MANY_RECTS.length; i++) {
+ final RectF rect = MANY_RECTS[i];
+ if (rect != null) {
+ builder.append(i, rect.left, rect.top, rect.right, rect.bottom);
+ }
+ }
+ final SparseRectFArray array = builder.build();
+ for (int i = 0; i < MANY_RECTS.length; i++) {
+ final RectF rect = MANY_RECTS[i];
+ assertEquals(rect, array.get(i));
+ }
+
+ // Make sure the builder reproduces an equivalent object.
+ final SparseRectFArray array2 = builder.build();
+ for (int i = 0; i < MANY_RECTS.length; i++) {
+ final RectF rect = MANY_RECTS[i];
+ assertEquals(rect, array2.get(i));
+ }
+ assertEqualRects(array, array2);
+
+ // Make sure the instance can be marshaled via {@link Parcel}.
+ final SparseRectFArray array3 = cloneViaParcel(array);
+ for (int i = 0; i < MANY_RECTS.length; i++) {
+ final RectF rect = MANY_RECTS[i];
+ assertEquals(rect, array3.get(i));
+ }
+ assertEqualRects(array, array3);
+
+ // Make sure the builder can be reset.
+ builder.reset();
+ assertNull(builder.build().get(0));
+ }
+
+ @SmallTest
+ public void testEquality() throws Exception {
+ // Empty array should be equal.
+ assertEqualRects(new SparseRectFArrayBuilder().build(),
+ new SparseRectFArrayBuilder().build());
+
+ assertEqualRects(
+ new SparseRectFArrayBuilder().append(100, 1.0f, 2.0f, 3.0f, 4.0f).build(),
+ new SparseRectFArrayBuilder().append(100, 1.0f, 2.0f, 3.0f, 4.0f).build());
+ assertNotEqualRects(
+ new SparseRectFArrayBuilder().append(100, 1.0f, 2.0f, 3.0f, 4.0f).build(),
+ new SparseRectFArrayBuilder().append(100, 2.0f, 2.0f, 3.0f, 4.0f).build());
+ assertNotEqualRects(
+ new SparseRectFArrayBuilder().append(100, 1.0f, 2.0f, 3.0f, 4.0f).build(),
+ new SparseRectFArrayBuilder().append(101, 1.0f, 2.0f, 3.0f, 4.0f).build());
+
+ assertEqualRects(
+ new SparseRectFArrayBuilder()
+ .append(100, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build(),
+ new SparseRectFArrayBuilder()
+ .append(100, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build());
+ assertNotEqualRects(
+ new SparseRectFArrayBuilder()
+ .append(100, 1.0f, 2.0f, 3.0f, 4.0f).build(),
+ new SparseRectFArrayBuilder()
+ .append(100, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build());
+ assertNotEqualRects(
+ new SparseRectFArrayBuilder()
+ .append(100, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build(),
+ new SparseRectFArrayBuilder()
+ .append(100, 1.0f, 2.0f, 3.0f, 4.0f).build());
+ assertNotEqualRects(
+ new SparseRectFArrayBuilder()
+ .append(100, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build(),
+ new SparseRectFArrayBuilder()
+ .append(100, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(101, 1.0f, 0.0f, 0.0f, 0.0f).build());
+ assertNotEqualRects(
+ new SparseRectFArrayBuilder()
+ .append(100, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(101, 1.0f, 0.0f, 0.0f, 0.0f).build(),
+ new SparseRectFArrayBuilder()
+ .append(100, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build());
+ assertNotEqualRects(
+ new SparseRectFArrayBuilder()
+ .append(100, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(101, 0.0f, 0.0f, 0.0f, 0.0f).build(),
+ new SparseRectFArrayBuilder()
+ .append(100, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(102, 0.0f, 0.0f, 0.0f, 0.0f).build());
+
+ assertEqualRects(
+ new SparseRectFArrayBuilder()
+ .append(1, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(1000, 0.0f, 0.0f, 0.0f, 0.0f)
+ .append(100000000, 0.0f, 0.0f, 0.0f, 0.0f)
+ .build(),
+ new SparseRectFArrayBuilder()
+ .append(1, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(1000, 0.0f, 0.0f, 0.0f, 0.0f)
+ .append(100000000, 0.0f, 0.0f, 0.0f, 0.0f)
+ .build());
+
+ assertNotEqualRects(
+ new SparseRectFArrayBuilder()
+ .append(1, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(1000, 0.0f, 0.0f, 0.0f, 0.0f)
+ .append(100000000, 0.0f, 0.0f, 0.0f, 0.0f)
+ .build(),
+ new SparseRectFArrayBuilder()
+ .append(1, 1.0f, 2.0f, 3.0f, 4.0f)
+ .build());
+ assertNotEqualRects(
+ new SparseRectFArrayBuilder()
+ .append(1, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(1000, 0.0f, 0.0f, 0.0f, 0.0f)
+ .append(100000000, 0.0f, 0.0f, 0.0f, 0.0f)
+ .build(),
+ new SparseRectFArrayBuilder()
+ .append(1, 1.0f, 2.0f, 3.0f, 4.0f)
+ .append(1000, 1.0f, 0.0f, 0.0f, 0.0f)
+ .append(100000000, 0.0f, 0.0f, 0.0f, 0.0f)
+ .build());
+ }
+
+ @SmallTest
+ public void testBuilderAppend() throws Exception {
+ // Key should be appended in ascending order.
+ try {
+ new SparseRectFArrayBuilder().append(10, 0, 0, 0, 0).append(0, 1, 2, 3, 4);
+ } catch (IllegalArgumentException ex) {
+ assertTrue(true);
+ }
+
+ try {
+ new SparseRectFArrayBuilder().append(10, 0, 0, 0, 0).append(10, 1, 2, 3, 4);
+ } catch (IllegalArgumentException ex) {
+ assertTrue(true);
+ }
+ }
+
+ private static void assertEqualRects(SparseRectFArray a, SparseRectFArray b) {
+ assertEquals(a, b);
+ if (a != null && b != null) {
+ assertEquals(a.hashCode(), b.hashCode());
+ }
+ }
+
+ private static void assertNotEqualRects(SparseRectFArray a, SparseRectFArray b) {
+ assertFalse(Objects.equals(a, b));
+ }
+
+ private static SparseRectFArray cloneViaParcel(final SparseRectFArray src) {
+ Parcel parcel = null;
+ try {
+ parcel = Parcel.obtain();
+ src.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ return new SparseRectFArray(parcel);
+ } finally {
+ if (parcel != null) {
+ parcel.recycle();
+ }
+ }
+ }
+}
diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd
index 4057736..6143b4b 100644
--- a/docs/html/guide/topics/manifest/uses-feature-element.jd
+++ b/docs/html/guide/topics/manifest/uses-feature-element.jd
@@ -584,9 +584,14 @@ is sensitive to delays or lag in sound input or output.</td>
</tr>
<tr>
<td><code>android.hardware.camera.any</code></td>
- <td>The application uses at least one camera facing in any direction. Use this
-in preference to <code>android.hardware.camera</code> if a back-facing camera is
-not required.</td>
+ <td>The application uses at least one camera facing in any direction, or an
+external camera device if one is connected. Use this in preference to
+<code>android.hardware.camera</code> if a back-facing camera is not required.
+ </td>
+</tr>
+<tr>
+ <td><code>android.hardware.camera.external</code></td>
+ <td>The application uses an external camera device if one is connected.</td>
</tr>
<tr>
diff --git a/docs/html/guide/topics/resources/string-resource.jd b/docs/html/guide/topics/resources/string-resource.jd
index 5a96ba1..e2326ec 100644
--- a/docs/html/guide/topics/resources/string-resource.jd
+++ b/docs/html/guide/topics/resources/string-resource.jd
@@ -20,9 +20,6 @@ your application with strings:</p>
information about styling and formatting strings, see the section about <a
href="#FormattingAndStyling">Formatting and Styling</a>.</p>
-
-
-
<h2 id="String">String</h2>
<p>A single string that can be referenced from the application or from other resource files (such
@@ -433,7 +430,7 @@ java.lang.Object...)">format</a>(res.getString(R.string.welcome_messages), usern
-<h3>Styling with HTML markup</h3>
+<h3 id="StylingWithHTML">Styling with HTML markup</h3>
<p>You can add styling to your strings with HTML markup. For example:</p>
<pre>
@@ -497,5 +494,107 @@ java.lang.Object...)">format</a>(res.getString(R.string.welcome_messages), escap
CharSequence styledText = Html.fromHtml(text);
</pre>
+<h2 id="StylingWithSpannables">Styling with Spannables</h2>
+<p>
+A {@link android.text.Spannable} is a text object that you can style with
+typeface properties such as color and font weight. You use
+{@link android.text.SpannableStringBuilder} to build
+your text and then apply styles defined in the {@link android.text.style}
+package to the text.
+</p>
+
+<p>You can use the following helper methods to set up much of the work
+of creating spannable text:</p>
+
+<pre style="pretty-print">
+/**
+ * Returns a CharSequence that concatenates the specified array of CharSequence
+ * objects and then applies a list of zero or more tags to the entire range.
+ *
+ * @param content an array of character sequences to apply a style to
+ * @param tags the styled span objects to apply to the content
+ * such as android.text.style.StyleSpan
+ *
+ */
+private static CharSequence apply(CharSequence[] content, Object... tags) {
+ SpannableStringBuilder text = new SpannableStringBuilder();
+ openTags(text, tags);
+ for (CharSequence item : content) {
+ text.append(item);
+ }
+ closeTags(text, tags);
+ return text;
+}
+
+/**
+ * Iterates over an array of tags and applies them to the beginning of the specified
+ * Spannable object so that future text appended to the text will have the styling
+ * applied to it. Do not call this method directly.
+ */
+private static void openTags(Spannable text, Object[] tags) {
+ for (Object tag : tags) {
+ text.setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK);
+ }
+}
+
+/**
+ * "Closes" the specified tags on a Spannable by updating the spans to be
+ * endpoint-exclusive so that future text appended to the end will not take
+ * on the same styling. Do not call this method directly.
+ */
+private static void closeTags(Spannable text, Object[] tags) {
+ int len = text.length();
+ for (Object tag : tags) {
+ if (len > 0) {
+ text.setSpan(tag, 0, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else {
+ text.removeSpan(tag);
+ }
+ }
+}
+</pre>
+<p>
+The following <code>bold</code>, <code>italic</code>, and <code>color</code>
+methods show you how to call the helper methods to apply
+styles defined in the {@link android.text.style} package. You
+can create similar methods to do other types of text styling.
+</p>
+
+<pre style="pretty-print">
+/**
+ * Returns a CharSequence that applies boldface to the concatenation
+ * of the specified CharSequence objects.
+ */
+public static CharSequence bold(CharSequence... content) {
+ return apply(content, new StyleSpan(Typeface.BOLD));
+}
+
+/**
+ * Returns a CharSequence that applies italics to the concatenation
+ * of the specified CharSequence objects.
+ */
+public static CharSequence italic(CharSequence... content) {
+ return apply(content, new StyleSpan(Typeface.ITALIC));
+}
+
+/**
+ * Returns a CharSequence that applies a foreground color to the
+ * concatenation of the specified CharSequence objects.
+ */
+public static CharSequence color(int color, CharSequence... content) {
+ return apply(content, new ForegroundColorSpan(color));
+}
+</pre>
+<p>
+Here's an example of how to chain these methods to create a character sequence
+with different types of styling applied to individual words:
+</p>
+
+<pre style="pretty-print">
+// Create an italic "hello, " a red "world",
+// and bold the entire sequence.
+CharSequence text = bold(italic(res.getString(R.string.hello)),
+ color(Color.RED, res.getString(R.string.world)));
+</pre> \ No newline at end of file
diff --git a/docs/html/guide/topics/ui/notifiers/notifications.jd b/docs/html/guide/topics/ui/notifiers/notifications.jd
index 3b1292e..59c2269 100644
--- a/docs/html/guide/topics/ui/notifiers/notifications.jd
+++ b/docs/html/guide/topics/ui/notifiers/notifications.jd
@@ -16,6 +16,7 @@ page.title=Notifications
<li><a href="#Required">Required notification contents</a></li>
<li><a href="#Optional">Optional notification contents and settings</a></li>
<li><a href="#Actions">Notification actions</a></li>
+ <li><a href="#Priority">Notification priority</a></li>
<li><a href="#SimpleNotification">Creating a simple notification</a></li>
<li><a href="#ApplyStyle">Applying a big view style to a notification</a></li>
<li><a href="#Compatibility">Handling compatibility</a></li>
@@ -290,6 +291,26 @@ page.title=Notifications
{@link android.support.v4.app.NotificationCompat.Builder}.
</p>
<!-- ------------------------------------------------------------------------------------------ -->
+<h3 id="Priority">Notification priority</h3>
+<p>
+ If you wish, you can set the priority of a notification. The priority acts
+ as a hint to the device UI about how the notification should be displayed.
+ To set a notification's priority, call {@link
+ android.support.v4.app.NotificationCompat.Builder#setPriority(int)
+ NotificationCompat.Builder.setPriority()} and pass in one of the {@link
+ android.support.v4.app.NotificationCompat} priority constants. There are
+ five priority levels, ranging from {@link
+ android.support.v4.app.NotificationCompat#PRIORITY_MIN} (-2) to {@link
+ android.support.v4.app.NotificationCompat#PRIORITY_MAX} (2); if not set, the
+ priority defaults to {@link
+ android.support.v4.app.NotificationCompat#PRIORITY_DEFAULT} (0).
+</p>
+<p> For information about setting an appropriate priority level, see "Correctly
+ set and manage notification priority" in the <a
+ href="{@docRoot}design/patterns/notifications.html">Notifications</a> Design
+ guide.
+</p>
+<!-- ------------------------------------------------------------------------------------------ -->
<h3 id="SimpleNotification">Creating a simple notification</h3>
<p>
The following snippet illustrates a simple notification that specifies an activity to open when
diff --git a/docs/html/wear/images/notif_summary_framed.png b/docs/html/wear/images/notif_summary_framed.png
new file mode 100644
index 0000000..17b1703
--- /dev/null
+++ b/docs/html/wear/images/notif_summary_framed.png
Binary files differ
diff --git a/docs/html/wear/notifications/stacks.jd b/docs/html/wear/notifications/stacks.jd
index 7f955f6..a2d34ce 100644
--- a/docs/html/wear/notifications/stacks.jd
+++ b/docs/html/wear/notifications/stacks.jd
@@ -2,8 +2,8 @@ page.title=Stacking Notifications
@jd:body
-<img src="{@docRoot}wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
-<img src="{@docRoot}wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
+<img src="{@docRoot}wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" alt="" />
+<img src="{@docRoot}wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" alt="" />
<p>When creating notifications for a handheld device, you should always aggregate similar
notifications into a single summary notification. For example, if your app creates notifications
@@ -29,20 +29,44 @@ Wear</a>.</p>
<p>To create a stack, call <a
href="{@docRoot}reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setGroup(java.lang.String, int)">
-<code>setGroup()</code></a> for each notification you want in the stack, passing the same
-group key. For example:</p>
+<code>setGroup()</code></a> for each notification you want in the stack and specify a
+group key. Then call <a href="{@docRoot}reference/android/preview/support/v4/app/NotificationManagerCompat.html#notify(int, android.app.Notification)"><code>notify()</code></a> to send it to the wearable.</p>
<pre style="clear:right">
final static String GROUP_KEY_EMAILS = "group_key_emails";
+// Build the notification and pass this builder to WearableNotifications.Builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
- .setContentTitle("New mail from " + sender)
- .setContentText(subject)
+ .setContentTitle("New mail from " + sender1)
+ .setContentText(subject1)
.setSmallIcon(R.drawable.new_mail);
-Notification notif = new WearableNotifications.Builder(builder)
+Notification notif1 = new WearableNotifications.Builder(builder)
.setGroup(GROUP_KEY_EMAILS)
.build();
+
+// Issue the notification
+NotificationManagerCompat notificationManager =
+ NotificationManagerCompat.from(this);
+notificationManager.notify(notificationId1, notif);
+</pre>
+
+<p>Later on, when you create another notification, specify
+the same group key. When you call <a href="{@docRoot}reference/android/preview/support/v4/app/NotificationManagerCompat.html#notify(int, android.app.Notification)"><code>notify()</code></a>, this notification appears
+in the same stack as the previous notification, instead of as a new card:</p>
+
+<pre style="clear:right">
+builder = new NotificationCompat.Builder(mContext)
+ .setContentTitle("New mail from " + sender2)
+ .setContentText(subject2)
+ .setSmallIcon(R.drawable.new_mail);
+
+// Use the same group as the previous notification
+Notification notif2 = new WearableNotifications.Builder(builder)
+ .setGroup(GROUP_KEY_EMAILS)
+ .build();
+
+notificationManager.notify(notificationId2, notif);
</pre>
<p>By default, notifications appear in the order in which you added them, with the most recent
@@ -54,19 +78,55 @@ href="{@docRoot}reference/android/preview/support/wearable/notifications/Wearabl
<h2 id="AddSummary">Add a Summary Notification</h2>
+<img src="{@docRoot}wear/images/notif_summary_framed.png" height="242" width="330" style="float:right;margin:0 0 20px 40px" alt="" />
+
<p>It's important that you still provide a summary notification that appears on handheld devices.
So in addition to adding each unique notification to the same stack group, also add a summary
notification, but set its order position to be <a
href="{@docRoot}reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY"><code>GROUP_ORDER_SUMMARY</code></a>.</p>
-<pre>
-Notification summaryNotification = new WearableNotifications.Builder(builder)
- .setGroup(GROUP_KEY_EMAILS, WearableNotifications.GROUP_ORDER_SUMMARY)
- .build();
-</pre>
+<p>This notification does not appear in your stack of notifications on the wearable, but
+appears as the only notification on the handheld device.</p>
-<p>This notification will not appear in your stack of notifications on the wearable, but
-appears as the only notification on the handheld device.
+<pre style="clear:right">
+Bitmap largeIcon = BitmapFactory.decodeResource(getResources(),
+ R.drawable.ic_large_icon);
+
+builder = new NotificationCompat.Builder(this)
+ .setSmallIcon(R.drawable.ic_small_icon)
+ .setLargeIcon(largeIcon);
+
+// Use the same group key and pass this builder to InboxStyle notification
+WearableNotifications.Builder wearableBuilder = new WearableNotifications
+ .Builder(builder)
+ .setGroup(GROUP_KEY_EMAILS,
+ WearableNotifications.GROUP_ORDER_SUMMARY);
+
+// Build the final notification to show on the handset
+Notification summaryNotification = new NotificationCompat.InboxStyle(
+ wearableBuilder.getCompatBuilder())
+ .addLine("Alex Faaborg Check this out")
+ .addLine("Jeff Chang Launch Party")
+ .setBigContentTitle("2 new messages")
+ .setSummaryText("johndoe@gmail.com")
+ .build();
+
+notificationManager.notify(notificationId3, summaryNotification);
+</pre>
+<p>
+This notification uses {@link android.support.v4.app.NotificationCompat.InboxStyle},
+which gives you an easy way to create notifications for email or messaging apps.
+You can use this style, another one defined in {@link android.support.v4.app.NotificationCompat},
+or no style for the summary notification.
+</p>
+
+<p class="note"><b>Tip:</b>
+To style the text like in the example screenshot, see
+<a href="{@docRoot}guide/topics/resources/string-resource.html#StylingWithHTML">Styling
+with HTML markup</a> and
+<a href="{@docRoot}guide/topics/resources/string-resource.html#StylingWithSpannables">Styling
+with Spannables</a>.
+</p>
</body>
-</html>
+</html> \ No newline at end of file
diff --git a/graphics/java/android/graphics/CanvasProperty.java b/graphics/java/android/graphics/CanvasProperty.java
index 99ea9b1..be86060 100644
--- a/graphics/java/android/graphics/CanvasProperty.java
+++ b/graphics/java/android/graphics/CanvasProperty.java
@@ -16,12 +16,15 @@
package android.graphics;
+import com.android.internal.util.VirtualRefBasePtr;
+
/**
* TODO: Make public?
* @hide
*/
public final class CanvasProperty<T> {
- private long mNativeContainer;
+
+ private VirtualRefBasePtr mProperty;
public static CanvasProperty<Float> createFloat(float initialValue) {
return new CanvasProperty<Float>(nCreateFloat(initialValue));
@@ -32,25 +35,14 @@ public final class CanvasProperty<T> {
}
private CanvasProperty(long nativeContainer) {
- mNativeContainer = nativeContainer;
+ mProperty = new VirtualRefBasePtr(nativeContainer);
}
/** @hide */
public long getNativeContainer() {
- return mNativeContainer;
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- nUnref(mNativeContainer);
- mNativeContainer = 0;
- } finally {
- super.finalize();
- }
+ return mProperty.get();
}
private static native long nCreateFloat(float initialValue);
private static native long nCreatePaint(long initialValuePaintPtr);
- private static native void nUnref(long ptr);
}
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 3f8c45c..0862cdd 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -18,6 +18,7 @@ package android.graphics;
import java.lang.ref.WeakReference;
+import android.annotation.Nullable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -132,14 +133,14 @@ public class SurfaceTexture {
* Register a callback to be invoked when a new image frame becomes available to the
* SurfaceTexture.
* <p>
- * This callback may be called on an arbitrary thread, so it is not
+ * The callback may be called on an arbitrary thread, so it is not
* safe to call {@link #updateTexImage} without first binding the OpenGL ES context to the
* thread invoking the callback.
* </p>
*
- * @param listener The listener to set.
+ * @param listener The listener to use, or null to remove the listener.
*/
- public void setOnFrameAvailableListener(OnFrameAvailableListener listener) {
+ public void setOnFrameAvailableListener(@Nullable OnFrameAvailableListener listener) {
setOnFrameAvailableListener(listener, null);
}
@@ -147,17 +148,18 @@ public class SurfaceTexture {
* Register a callback to be invoked when a new image frame becomes available to the
* SurfaceTexture.
* <p>
- * If no handler is specified, then this callback may be called on an arbitrary thread,
+ * If a handler is specified, the callback will be invoked on that handler's thread.
+ * If no handler is specified, then the callback may be called on an arbitrary thread,
* so it is not safe to call {@link #updateTexImage} without first binding the OpenGL ES
* context to the thread invoking the callback.
* </p>
*
- * @param listener The listener to set.
+ * @param listener The listener to use, or null to remove the listener.
* @param handler The handler on which the listener should be invoked, or null
* to use an arbitrary thread.
*/
- public void setOnFrameAvailableListener(final OnFrameAvailableListener listener,
- Handler handler) {
+ public void setOnFrameAvailableListener(@Nullable final OnFrameAvailableListener listener,
+ @Nullable Handler handler) {
if (listener != null) {
// Although we claim the thread is arbitrary, earlier implementation would
// prefer to send the callback on the creating looper or the main looper
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
new file mode 100644
index 0000000..46e3401
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -0,0 +1,524 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.LongSparseLongArray;
+import android.util.SparseIntArray;
+import android.util.StateSet;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Drawable containing a set of Drawable keyframes where the currently displayed
+ * keyframe is chosen based on the current state set. Animations between
+ * keyframes may optionally be defined using transition elements.
+ * <p>
+ * This drawable can be defined in an XML file with the <code>
+ * &lt;animated-selector></code> element. Each keyframe Drawable is defined in a
+ * nested <code>&lt;item></code> element. Transitions are defined in a nested
+ * <code>&lt;transition></code> element.
+ *
+ * @attr ref android.R.styleable#DrawableStates_state_focused
+ * @attr ref android.R.styleable#DrawableStates_state_window_focused
+ * @attr ref android.R.styleable#DrawableStates_state_enabled
+ * @attr ref android.R.styleable#DrawableStates_state_checkable
+ * @attr ref android.R.styleable#DrawableStates_state_checked
+ * @attr ref android.R.styleable#DrawableStates_state_selected
+ * @attr ref android.R.styleable#DrawableStates_state_activated
+ * @attr ref android.R.styleable#DrawableStates_state_active
+ * @attr ref android.R.styleable#DrawableStates_state_single
+ * @attr ref android.R.styleable#DrawableStates_state_first
+ * @attr ref android.R.styleable#DrawableStates_state_middle
+ * @attr ref android.R.styleable#DrawableStates_state_last
+ * @attr ref android.R.styleable#DrawableStates_state_pressed
+ */
+public class AnimatedStateListDrawable extends StateListDrawable {
+ private static final String ELEMENT_TRANSITION = "transition";
+ private static final String ELEMENT_ITEM = "item";
+
+ private AnimatedStateListState mState;
+
+ /** The currently running animation, if any. */
+ private ObjectAnimator mAnim;
+
+ /** Index to be set after the animation ends. */
+ private int mAnimToIndex = -1;
+
+ /** Index away from which we are animating. */
+ private int mAnimFromIndex = -1;
+
+ private boolean mMutated;
+
+ public AnimatedStateListDrawable() {
+ this(null, null);
+ }
+
+ /**
+ * Add a new drawable to the set of keyframes.
+ *
+ * @param stateSet An array of resource IDs to associate with the keyframe
+ * @param drawable The drawable to show when in the specified state
+ * @param id The unique identifier for the keyframe
+ */
+ public void addState(int[] stateSet, Drawable drawable, int id) {
+ if (drawable != null) {
+ mState.addStateSet(stateSet, drawable, id);
+ onStateChange(getState());
+ }
+ }
+
+ /**
+ * Adds a new transition between keyframes.
+ *
+ * @param fromId Unique identifier of the starting keyframe
+ * @param toId Unique identifier of the ending keyframe
+ * @param anim An AnimationDrawable to use as a transition
+ * @param reversible Whether the transition can be reversed
+ */
+ public void addTransition(int fromId, int toId, AnimationDrawable anim, boolean reversible) {
+ mState.addTransition(fromId, toId, anim, reversible);
+ }
+
+ @Override
+ public boolean isStateful() {
+ return true;
+ }
+
+ @Override
+ protected boolean onStateChange(int[] stateSet) {
+ final int keyframeIndex = mState.indexOfKeyframe(stateSet);
+ if (keyframeIndex == getCurrentIndex()) {
+ return false;
+ }
+
+ if (selectTransition(keyframeIndex)) {
+ return true;
+ }
+
+ if (selectDrawable(keyframeIndex)) {
+ return true;
+ }
+
+ return super.onStateChange(stateSet);
+ }
+
+ private boolean selectTransition(int toIndex) {
+ if (mAnim != null) {
+ if (toIndex == mAnimToIndex) {
+ // Already animating to that keyframe.
+ return true;
+ } else if (toIndex == mAnimFromIndex) {
+ // Reverse the current animation.
+ mAnim.reverse();
+ mAnimFromIndex = mAnimToIndex;
+ mAnimToIndex = toIndex;
+ return true;
+ }
+
+ // Changing animation, end the current animation.
+ mAnim.end();
+ }
+
+ final AnimatedStateListState state = mState;
+ final int fromIndex = getCurrentIndex();
+ final int fromId = state.getKeyframeIdAt(fromIndex);
+ final int toId = state.getKeyframeIdAt(toIndex);
+
+ if (toId == 0 || fromId == 0) {
+ // Missing a keyframe ID.
+ return false;
+ }
+
+ final int transitionIndex = state.indexOfTransition(fromId, toId);
+ if (transitionIndex < 0 || !selectDrawable(transitionIndex)) {
+ // Couldn't select a transition.
+ return false;
+ }
+
+ final Drawable d = getCurrent();
+ if (!(d instanceof AnimationDrawable)) {
+ // Transition isn't an animation.
+ return false;
+ }
+
+ final AnimationDrawable ad = (AnimationDrawable) d;
+ final boolean reversed = mState.isTransitionReversed(fromId, toId);
+ final int frameCount = ad.getNumberOfFrames();
+ final int fromFrame = reversed ? frameCount - 1 : 0;
+ final int toFrame = reversed ? 0 : frameCount - 1;
+
+ final FrameInterpolator interp = new FrameInterpolator(ad, reversed);
+ final ObjectAnimator anim = ObjectAnimator.ofInt(ad, "currentIndex", fromFrame, toFrame);
+ anim.setAutoCancel(true);
+ anim.setDuration(interp.getTotalDuration());
+ anim.addListener(mAnimListener);
+ anim.setInterpolator(interp);
+ anim.start();
+
+ mAnim = anim;
+ mAnimFromIndex = fromIndex;
+ mAnimToIndex = toIndex;
+ return true;
+ }
+
+ @Override
+ public void jumpToCurrentState() {
+ super.jumpToCurrentState();
+
+ if (mAnim != null) {
+ mAnim.end();
+ }
+ }
+
+ @Override
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
+ final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedStateListDrawable);
+
+ super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedStateListDrawable_visible);
+
+ final StateListState stateListState = getStateListState();
+ stateListState.setVariablePadding(a.getBoolean(
+ R.styleable.AnimatedStateListDrawable_variablePadding, false));
+ stateListState.setConstantSize(a.getBoolean(
+ R.styleable.AnimatedStateListDrawable_constantSize, false));
+ stateListState.setEnterFadeDuration(a.getInt(
+ R.styleable.AnimatedStateListDrawable_enterFadeDuration, 0));
+ stateListState.setExitFadeDuration(a.getInt(
+ R.styleable.AnimatedStateListDrawable_exitFadeDuration, 0));
+
+ setDither(a.getBoolean(R.styleable.AnimatedStateListDrawable_dither, true));
+ setAutoMirrored(a.getBoolean(R.styleable.AnimatedStateListDrawable_autoMirrored, false));
+
+ a.recycle();
+
+ int type;
+
+ final int innerDepth = parser.getDepth() + 1;
+ int depth;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && ((depth = parser.getDepth()) >= innerDepth
+ || type != XmlPullParser.END_TAG)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ if (depth > innerDepth) {
+ continue;
+ }
+
+ if (parser.getName().equals(ELEMENT_ITEM)) {
+ parseItem(r, parser, attrs, theme);
+ } else if (parser.getName().equals(ELEMENT_TRANSITION)) {
+ parseTransition(r, parser, attrs, theme);
+ }
+ }
+
+ onStateChange(getState());
+ }
+
+ private int parseTransition(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
+ int drawableRes = 0;
+ int fromId = 0;
+ int toId = 0;
+ boolean reversible = false;
+
+ final int numAttrs = attrs.getAttributeCount();
+ for (int i = 0; i < numAttrs; i++) {
+ final int stateResId = attrs.getAttributeNameResource(i);
+ switch (stateResId) {
+ case 0:
+ break;
+ case R.attr.fromId:
+ fromId = attrs.getAttributeResourceValue(i, 0);
+ break;
+ case R.attr.toId:
+ toId = attrs.getAttributeResourceValue(i, 0);
+ break;
+ case R.attr.drawable:
+ drawableRes = attrs.getAttributeResourceValue(i, 0);
+ break;
+ case R.attr.reversible:
+ reversible = attrs.getAttributeBooleanValue(i, false);
+ break;
+ }
+ }
+
+ final Drawable dr;
+ if (drawableRes != 0) {
+ dr = r.getDrawable(drawableRes);
+ } else {
+ int type;
+ while ((type = parser.next()) == XmlPullParser.TEXT) {
+ }
+ if (type != XmlPullParser.START_TAG) {
+ throw new XmlPullParserException(
+ parser.getPositionDescription()
+ + ": <item> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+ }
+
+ final AnimationDrawable anim;
+ if (dr instanceof AnimationDrawable) {
+ anim = (AnimationDrawable) dr;
+ } else {
+ throw new XmlPullParserException(parser.getPositionDescription()
+ + ": <transition> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable of type <animation>");
+ }
+
+ return mState.addTransition(fromId, toId, anim, reversible);
+ }
+
+ private int parseItem(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+ throws XmlPullParserException, IOException {
+ int drawableRes = 0;
+ int keyframeId = 0;
+
+ int j = 0;
+ final int numAttrs = attrs.getAttributeCount();
+ int[] states = new int[numAttrs];
+ for (int i = 0; i < numAttrs; i++) {
+ final int stateResId = attrs.getAttributeNameResource(i);
+ switch (stateResId) {
+ case 0:
+ break;
+ case R.attr.id:
+ keyframeId = attrs.getAttributeResourceValue(i, 0);
+ break;
+ case R.attr.drawable:
+ drawableRes = attrs.getAttributeResourceValue(i, 0);
+ break;
+ default:
+ final boolean hasState = attrs.getAttributeBooleanValue(i, false);
+ states[j++] = hasState ? stateResId : -stateResId;
+ }
+ }
+ states = StateSet.trimStateSet(states, j);
+
+ final Drawable dr;
+ if (drawableRes != 0) {
+ dr = r.getDrawable(drawableRes);
+ } else {
+ int type;
+ while ((type = parser.next()) == XmlPullParser.TEXT) {
+ }
+ if (type != XmlPullParser.START_TAG) {
+ throw new XmlPullParserException(
+ parser.getPositionDescription()
+ + ": <item> tag requires a 'drawable' attribute or "
+ + "child tag defining a drawable");
+ }
+ dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+ }
+
+ return mState.addStateSet(states, dr, keyframeId);
+ }
+
+ @Override
+ public Drawable mutate() {
+ if (!mMutated) {
+ final AnimatedStateListState newState = new AnimatedStateListState(mState, this, null);
+ setConstantState(newState);
+ mMutated = true;
+ }
+
+ return this;
+ }
+
+ private final AnimatorListenerAdapter mAnimListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator anim) {
+ selectDrawable(mAnimToIndex);
+
+ mAnimToIndex = -1;
+ mAnimFromIndex = -1;
+ mAnim = null;
+ }
+ };
+
+ static class AnimatedStateListState extends StateListState {
+ private static final int REVERSE_SHIFT = 32;
+ private static final int REVERSE_MASK = 0x1;
+
+ final LongSparseLongArray mTransitions;
+ final SparseIntArray mStateIds;
+
+ AnimatedStateListState(AnimatedStateListState orig, AnimatedStateListDrawable owner,
+ Resources res) {
+ super(orig, owner, res);
+
+ if (orig != null) {
+ mTransitions = orig.mTransitions.clone();
+ mStateIds = orig.mStateIds.clone();
+ } else {
+ mTransitions = new LongSparseLongArray();
+ mStateIds = new SparseIntArray();
+ }
+ }
+
+ int addTransition(int fromId, int toId, AnimationDrawable anim, boolean reversible) {
+ final int pos = super.addChild(anim);
+ final long keyFromTo = generateTransitionKey(fromId, toId);
+ mTransitions.append(keyFromTo, pos);
+
+ if (reversible) {
+ final long keyToFrom = generateTransitionKey(toId, fromId);
+ mTransitions.append(keyToFrom, pos | (1L << REVERSE_SHIFT));
+ }
+
+ return addChild(anim);
+ }
+
+ int addStateSet(int[] stateSet, Drawable drawable, int id) {
+ final int index = super.addStateSet(stateSet, drawable);
+ mStateIds.put(index, id);
+ return index;
+ }
+
+ int indexOfKeyframe(int[] stateSet) {
+ final int index = super.indexOfStateSet(stateSet);
+ if (index >= 0) {
+ return index;
+ }
+
+ return super.indexOfStateSet(StateSet.WILD_CARD);
+ }
+
+ int getKeyframeIdAt(int index) {
+ return index < 0 ? 0 : mStateIds.get(index, 0);
+ }
+
+ int indexOfTransition(int fromId, int toId) {
+ final long keyFromTo = generateTransitionKey(fromId, toId);
+ return (int) mTransitions.get(keyFromTo, -1);
+ }
+
+ boolean isTransitionReversed(int fromId, int toId) {
+ final long keyFromTo = generateTransitionKey(fromId, toId);
+ return (mTransitions.get(keyFromTo, -1) >> REVERSE_SHIFT & REVERSE_MASK) == 1;
+ }
+
+ @Override
+ public Drawable newDrawable() {
+ return new AnimatedStateListDrawable(this, null);
+ }
+
+ @Override
+ public Drawable newDrawable(Resources res) {
+ return new AnimatedStateListDrawable(this, res);
+ }
+
+ private static long generateTransitionKey(int fromId, int toId) {
+ return (long) fromId << 32 | toId;
+ }
+ }
+
+ void setConstantState(AnimatedStateListState state) {
+ super.setConstantState(state);
+
+ mState = state;
+ }
+
+ private AnimatedStateListDrawable(AnimatedStateListState state, Resources res) {
+ super(null);
+
+ final AnimatedStateListState newState = new AnimatedStateListState(state, this, res);
+ setConstantState(newState);
+ onStateChange(getState());
+ jumpToCurrentState();
+ }
+
+ /**
+ * Interpolates between frames with respect to their individual durations.
+ */
+ private static class FrameInterpolator implements TimeInterpolator {
+ private int[] mFrameTimes;
+ private int mFrames;
+ private int mTotalDuration;
+
+ public FrameInterpolator(AnimationDrawable d, boolean reversed) {
+ updateFrames(d, reversed);
+ }
+
+ public int updateFrames(AnimationDrawable d, boolean reversed) {
+ final int N = d.getNumberOfFrames();
+ mFrames = N;
+
+ if (mFrameTimes == null || mFrameTimes.length < N) {
+ mFrameTimes = new int[N];
+ }
+
+ final int[] frameTimes = mFrameTimes;
+ int totalDuration = 0;
+ for (int i = 0; i < N; i++) {
+ final int duration = d.getDuration(reversed ? N - i - 1 : i);
+ frameTimes[i] = duration;
+ totalDuration += duration;
+ }
+
+ mTotalDuration = totalDuration;
+ return totalDuration;
+ }
+
+ public int getTotalDuration() {
+ return mTotalDuration;
+ }
+
+ @Override
+ public float getInterpolation(float input) {
+ final int elapsed = (int) (input * mTotalDuration + 0.5f);
+ final int N = mFrames;
+ final int[] frameTimes = mFrameTimes;
+
+ // Find the current frame and remaining time within that frame.
+ int remaining = elapsed;
+ int i = 0;
+ while (i < N && remaining >= frameTimes[i]) {
+ remaining -= frameTimes[i];
+ i++;
+ }
+
+ // Remaining time is relative of total duration.
+ final float frameElapsed;
+ if (i < N) {
+ frameElapsed = remaining / (float) mTotalDuration;
+ } else {
+ frameElapsed = 0;
+ }
+
+ return i / (float) N + frameElapsed;
+ }
+ }
+}
+
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 3f94e26..da4bc10 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -94,7 +94,7 @@ public class AnimationDrawable extends DrawableContainer implements Runnable, An
boolean changed = super.setVisible(visible, restart);
if (visible) {
if (changed || restart) {
- setFrame(0, true, true);
+ setFrame(0, true, mCurFrame >= 0);
}
} else {
unscheduleSelf(this);
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index b9d5e19..b939636 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1039,6 +1039,8 @@ public abstract class Drawable {
final String name = parser.getName();
if (name.equals("selector")) {
drawable = new StateListDrawable();
+ } else if (name.equals("animated-selector")) {
+ drawable = new AnimatedStateListDrawable();
} else if (name.equals("level-list")) {
drawable = new LevelListDrawable();
} else if (name.equals("layer-list")) {
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 1f8b51d..08fc99d 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -359,6 +359,16 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
mDrawableContainerState.getOpacity();
}
+ /** @hide */
+ public void setCurrentIndex(int index) {
+ selectDrawable(index);
+ }
+
+ /** @hide */
+ public int getCurrentIndex() {
+ return mCurIndex;
+ }
+
public boolean selectDrawable(int idx) {
if (idx == mCurIndex) {
return false;
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 271af2b..f22a063 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -55,8 +55,9 @@ import android.util.StateSet;
* @attr ref android.R.styleable#DrawableStates_state_pressed
*/
public class StateListDrawable extends DrawableContainer {
+ private static final String TAG = StateListDrawable.class.getSimpleName();
+
private static final boolean DEBUG = false;
- private static final String TAG = "StateListDrawable";
/**
* To be proper, we should have a getter for dither (and alpha, etc.)
@@ -69,7 +70,8 @@ public class StateListDrawable extends DrawableContainer {
* to improve the quality at negligible cost.
*/
private static final boolean DEFAULT_DITHER = true;
- private final StateListState mStateListState;
+
+ private StateListState mStateListState;
private boolean mMutated;
public StateListDrawable() {
@@ -274,7 +276,7 @@ public class StateListDrawable extends DrawableContainer {
mStateListState.setLayoutDirection(layoutDirection);
}
- static final class StateListState extends DrawableContainerState {
+ static class StateListState extends DrawableContainerState {
int[][] mStateSets;
StateListState(StateListState orig, StateListDrawable owner, Resources res) {
@@ -293,7 +295,7 @@ public class StateListDrawable extends DrawableContainer {
return pos;
}
- private int indexOfStateSet(int[] stateSet) {
+ int indexOfStateSet(int[] stateSet) {
final int[][] stateSets = mStateSets;
final int N = getChildCount();
for (int i = 0; i < N; i++) {
@@ -323,11 +325,26 @@ public class StateListDrawable extends DrawableContainer {
}
}
+ void setConstantState(StateListState state) {
+ super.setConstantState(state);
+
+ mStateListState = state;
+ }
+
private StateListDrawable(StateListState state, Resources res) {
- StateListState as = new StateListState(state, this, res);
- mStateListState = as;
- setConstantState(as);
+ final StateListState newState = new StateListState(state, this, res);
+ setConstantState(newState);
onStateChange(getState());
}
+
+ /**
+ * This constructor exists so subclasses can avoid calling the default
+ * constructor and setting up a StateListDrawable-specific constant state.
+ */
+ StateListDrawable(StateListState state) {
+ if (state != null) {
+ setConstantState(state);
+ }
+ }
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 098753b..6aad5fb 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2452,15 +2452,19 @@ String8 ResTable_config::toString() const {
if (mcc != 0) {
if (res.size() > 0) res.append("-");
- res.appendFormat("%dmcc", dtohs(mcc));
+ res.appendFormat("mcc%d", dtohs(mcc));
}
if (mnc != 0) {
if (res.size() > 0) res.append("-");
- res.appendFormat("%dmnc", dtohs(mnc));
+ res.appendFormat("mnc%d", dtohs(mnc));
}
+
char localeStr[RESTABLE_MAX_LOCALE_LEN];
getBcp47Locale(localeStr);
- res.append(localeStr);
+ if (strlen(localeStr) > 0) {
+ if (res.size() > 0) res.append("-");
+ res.append(localeStr);
+ }
if ((screenLayout&MASK_LAYOUTDIR) != 0) {
if (res.size() > 0) res.append("-");
@@ -2627,6 +2631,20 @@ String8 ResTable_config::toString() const {
break;
}
}
+ if ((inputFlags&MASK_KEYSHIDDEN) != 0) {
+ if (res.size() > 0) res.append("-");
+ switch (inputFlags&MASK_KEYSHIDDEN) {
+ case ResTable_config::KEYSHIDDEN_NO:
+ res.append("keysexposed");
+ break;
+ case ResTable_config::KEYSHIDDEN_YES:
+ res.append("keyshidden");
+ break;
+ case ResTable_config::KEYSHIDDEN_SOFT:
+ res.append("keyssoft");
+ break;
+ }
+ }
if (keyboard != KEYBOARD_ANY) {
if (res.size() > 0) res.append("-");
switch (keyboard) {
@@ -2644,17 +2662,18 @@ String8 ResTable_config::toString() const {
break;
}
}
- if ((inputFlags&MASK_KEYSHIDDEN) != 0) {
+ if ((inputFlags&MASK_NAVHIDDEN) != 0) {
if (res.size() > 0) res.append("-");
- switch (inputFlags&MASK_KEYSHIDDEN) {
- case ResTable_config::KEYSHIDDEN_NO:
- res.append("keysexposed");
+ switch (inputFlags&MASK_NAVHIDDEN) {
+ case ResTable_config::NAVHIDDEN_NO:
+ res.append("navexposed");
break;
- case ResTable_config::KEYSHIDDEN_YES:
- res.append("keyshidden");
+ case ResTable_config::NAVHIDDEN_YES:
+ res.append("navhidden");
break;
- case ResTable_config::KEYSHIDDEN_SOFT:
- res.append("keyssoft");
+ default:
+ res.appendFormat("inputFlagsNavHidden=%d",
+ dtohs(inputFlags&MASK_NAVHIDDEN));
break;
}
}
@@ -2678,21 +2697,6 @@ String8 ResTable_config::toString() const {
break;
}
}
- if ((inputFlags&MASK_NAVHIDDEN) != 0) {
- if (res.size() > 0) res.append("-");
- switch (inputFlags&MASK_NAVHIDDEN) {
- case ResTable_config::NAVHIDDEN_NO:
- res.append("navsexposed");
- break;
- case ResTable_config::NAVHIDDEN_YES:
- res.append("navhidden");
- break;
- default:
- res.appendFormat("inputFlagsNavHidden=%d",
- dtohs(inputFlags&MASK_NAVHIDDEN));
- break;
- }
- }
if (screenSize != 0) {
if (res.size() > 0) res.append("-");
res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight));
@@ -5503,7 +5507,25 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
if (package == NULL) {
return (mError=NO_MEMORY);
}
-
+
+ if (idmap_id == 0) {
+ err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
+ header->dataEnd-(base+dtohl(pkg->typeStrings)));
+ if (err != NO_ERROR) {
+ delete group;
+ delete package;
+ return (mError=err);
+ }
+
+ err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
+ header->dataEnd-(base+dtohl(pkg->keyStrings)));
+ if (err != NO_ERROR) {
+ delete group;
+ delete package;
+ return (mError=err);
+ }
+ }
+
if (id == 0) {
// This is a library so assign an ID
id = mNextPackageId++;
@@ -5521,21 +5543,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
return (mError=NO_MEMORY);
}
- err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
- header->dataEnd-(base+dtohl(pkg->typeStrings)));
- if (err != NO_ERROR) {
- delete group;
- delete package;
- return (mError=err);
- }
- err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
- header->dataEnd-(base+dtohl(pkg->keyStrings)));
- if (err != NO_ERROR) {
- delete group;
- delete package;
- return (mError=err);
- }
-
//printf("Adding new package id %d at index %d\n", id, idx);
err = mPackageGroups.add(group);
if (err < NO_ERROR) {
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index eb0cac8..2cadf09 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -6,6 +6,7 @@ include $(CLEAR_VARS)
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SRC_FILES := \
utils/Blur.cpp \
+ utils/GLUtils.cpp \
utils/SortedListImpl.cpp \
thread/TaskManager.cpp \
font/CacheTexture.cpp \
@@ -53,7 +54,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
TextureCache.cpp \
TextDropShadowCache.cpp
- # RenderThread stuff
+# RenderThread stuff
LOCAL_SRC_FILES += \
renderthread/CanvasContext.cpp \
renderthread/DrawFrameTask.cpp \
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 0b074cc..86fc7c3 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -17,13 +17,13 @@
#define ANIMATOR_H
#include <cutils/compiler.h>
+#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
#include "CanvasProperty.h"
#include "Interpolator.h"
#include "TreeInfo.h"
#include "utils/Macros.h"
-#include "utils/VirtualLightRefBase.h"
namespace android {
namespace uirenderer {
diff --git a/libs/hwui/CanvasProperty.h b/libs/hwui/CanvasProperty.h
index 2e1d176..6074394 100644
--- a/libs/hwui/CanvasProperty.h
+++ b/libs/hwui/CanvasProperty.h
@@ -16,8 +16,9 @@
#ifndef CANVASPROPERTY_H
#define CANVASPROPERTY_H
+#include <utils/RefBase.h>
+
#include "utils/Macros.h"
-#include "utils/VirtualLightRefBase.h"
#include <SkPaint.h>
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index eaeb772..b2ead5b 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -41,7 +41,6 @@
#include "Matrix.h"
#include "DeferredDisplayList.h"
#include "RenderProperties.h"
-#include "utils/VirtualLightRefBase.h"
class SkBitmap;
class SkPaint;
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index c2ce6ed..2391e80 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -58,7 +58,7 @@ DisplayListData* DisplayListRenderer::finishRecording() {
void DisplayListRenderer::setViewport(int width, int height) {
// TODO: DisplayListRenderer shouldn't have a projection matrix, as it should never be used
- mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1);
+ mProjectionMatrix.loadOrtho(0, width, height, 0, -1, 1);
initializeViewport(width, height);
}
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index f06106b..2268386 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -482,8 +482,8 @@ void Matrix4::decomposeScale(float& sx, float& sy) const {
sy = copysignf(sqrtf(len), data[mat4::kScaleY]);
}
-void Matrix4::dump() const {
- ALOGD("Matrix4[simple=%d, type=0x%x", isSimple(), getType());
+void Matrix4::dump(const char* label) const {
+ ALOGD("%s[simple=%d, type=0x%x", label ? label : "Matrix4", isSimple(), getType());
ALOGD(" %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]);
ALOGD(" %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]);
ALOGD(" %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]);
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 26cb05f..e33a001 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -209,7 +209,7 @@ public:
void decomposeScale(float& sx, float& sy) const;
- void dump() const;
+ void dump(const char* label = NULL) const;
static const Matrix4& identity();
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 87b07b3..20b038d 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -37,6 +37,7 @@
#include "PathTessellator.h"
#include "Properties.h"
#include "ShadowTessellator.h"
+#include "utils/GLUtils.h"
#include "Vector.h"
#include "VertexBuffer.h"
@@ -170,7 +171,7 @@ void OpenGLRenderer::setViewport(int width, int height) {
}
void OpenGLRenderer::initViewport(int width, int height) {
- mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1);
+ mProjectionMatrix.loadOrtho(0, width, height, 0, -1, 1);
initializeViewport(width, height);
}
@@ -296,24 +297,7 @@ void OpenGLRenderer::finish() {
if (!suppressErrorChecks()) {
#if DEBUG_OPENGL
- GLenum status = GL_NO_ERROR;
- while ((status = glGetError()) != GL_NO_ERROR) {
- ALOGD("GL error from OpenGLRenderer: 0x%x", status);
- switch (status) {
- case GL_INVALID_ENUM:
- ALOGE(" GL_INVALID_ENUM");
- break;
- case GL_INVALID_VALUE:
- ALOGE(" GL_INVALID_VALUE");
- break;
- case GL_INVALID_OPERATION:
- ALOGE(" GL_INVALID_OPERATION");
- break;
- case GL_OUT_OF_MEMORY:
- ALOGE(" Out of memory!");
- break;
- }
- }
+ GLUtils::dumpGLErrors();
#endif
#if DEBUG_MEMORY_USAGE
@@ -644,7 +628,7 @@ void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot&
if (restoreOrtho) {
const Rect& r = restored.viewport;
glViewport(r.left, r.top, r.right, r.bottom);
- mViewProjMatrix.load(removed.orthoMatrix); // TODO: should ortho be stored in 'restored'?
+ mProjectionMatrix.load(removed.orthoMatrix); // TODO: should ortho be stored in 'restored'?
}
if (restoreClip) {
@@ -870,7 +854,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
mSnapshot->height = bounds.getHeight();
- mSnapshot->orthoMatrix.load(mViewProjMatrix);
+ mSnapshot->orthoMatrix.load(mProjectionMatrix);
endTiling();
debugOverdraw(false, false);
@@ -900,8 +884,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
// Change the ortho projection
glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
- // TODO: determine best way to support 3d drawing within HW layers
- mViewProjMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f);
+ mProjectionMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f);
return true;
}
@@ -1705,17 +1688,17 @@ void OpenGLRenderer::setupDrawDirtyRegionsDisabled() {
void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset,
float left, float top, float right, float bottom, bool ignoreTransform) {
- mModelView.loadTranslate(left, top, 0.0f);
+ mModelViewMatrix.loadTranslate(left, top, 0.0f);
if (mode == kModelViewMode_TranslateAndScale) {
- mModelView.scale(right - left, bottom - top, 1.0f);
+ mModelViewMatrix.scale(right - left, bottom - top, 1.0f);
}
bool dirty = right - left > 0.0f && bottom - top > 0.0f;
if (!ignoreTransform) {
- mCaches.currentProgram->set(mViewProjMatrix, mModelView, *currentTransform(), offset);
+ mCaches.currentProgram->set(mProjectionMatrix, mModelViewMatrix, *currentTransform(), offset);
if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, *currentTransform());
} else {
- mCaches.currentProgram->set(mViewProjMatrix, mModelView, mat4::identity(), offset);
+ mCaches.currentProgram->set(mProjectionMatrix, mModelViewMatrix, mat4::identity(), offset);
if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom);
}
}
@@ -1740,11 +1723,11 @@ void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
// compensate.
mat4 modelViewWithoutTransform;
modelViewWithoutTransform.loadInverse(*currentTransform());
- modelViewWithoutTransform.multiply(mModelView);
- mModelView.load(modelViewWithoutTransform);
+ modelViewWithoutTransform.multiply(mModelViewMatrix);
+ mModelViewMatrix.load(modelViewWithoutTransform);
}
mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
- mModelView, *mSnapshot, &mTextureUnit);
+ mModelViewMatrix, *mSnapshot, &mTextureUnit);
}
}
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 1d46945..4f7f01e 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -930,8 +930,8 @@ private:
*/
Texture* getTexture(const SkBitmap* bitmap);
- // Matrix used for view/projection in shaders
- mat4 mViewProjMatrix;
+ // Ortho matrix used for projection in shaders
+ mat4 mProjectionMatrix;
/**
* Model-view matrix used to position/size objects
@@ -939,15 +939,15 @@ private:
* Stores operation-local modifications to the draw matrix that aren't incorporated into the
* currentTransform().
*
- * If generated with kModelViewMode_Translate, the mModelView will reflect an x/y offset,
+ * If generated with kModelViewMode_Translate, mModelViewMatrix will reflect an x/y offset,
* e.g. the offset in drawLayer(). If generated with kModelViewMode_TranslateAndScale,
- * mModelView will reflect a translation and scale, e.g. the translation and scale required to
- * make VBO 0 (a rect of (0,0,1,1)) scaled to match the x,y offset, and width/height of a
- * bitmap.
+ * mModelViewMatrix will reflect a translation and scale, e.g. the translation and scale
+ * required to make VBO 0 (a rect of (0,0,1,1)) scaled to match the x,y offset, and width/height
+ * of a bitmap.
*
* Used as input to SkiaShader transformation.
*/
- mat4 mModelView;
+ mat4 mModelViewMatrix;
// State used to define the clipping region
Rect mTilingClip;
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 92964a8..f38d8b7 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -175,6 +175,10 @@ public:
bottom += dy;
}
+ void inset(float delta) {
+ outset(-delta);
+ }
+
void outset(float delta) {
left -= delta;
top -= delta;
@@ -230,8 +234,8 @@ public:
bottom = ceilf(bottom);
}
- void dump() const {
- ALOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom);
+ void dump(const char* label) const {
+ ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom);
}
private:
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 159903c..bc62ee1 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -45,7 +45,6 @@
#include "DisplayList.h"
#include "RenderProperties.h"
#include "TreeInfo.h"
-#include "utils/VirtualLightRefBase.h"
class SkBitmap;
class SkPaint;
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index d26ee38..6bfa203 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -27,9 +27,15 @@ namespace uirenderer {
// Constructors
///////////////////////////////////////////////////////////////////////////////
-Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0),
- invisible(false), empty(false), alpha(1.0f) {
-
+Snapshot::Snapshot()
+ : flags(0)
+ , previous(NULL)
+ , layer(NULL)
+ , fbo(0)
+ , invisible(false)
+ , empty(false)
+ , height(0)
+ , alpha(1.0f) {
transform = &mTransformRoot;
clipRect = &mClipRectRoot;
region = NULL;
@@ -40,10 +46,16 @@ Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0),
* Copies the specified snapshot/ The specified snapshot is stored as
* the previous snapshot.
*/
-Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags):
- flags(0), previous(s), layer(s->layer), fbo(s->fbo),
- invisible(s->invisible), empty(false),
- viewport(s->viewport), height(s->height), alpha(s->alpha) {
+Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
+ : flags(0)
+ , previous(s)
+ , layer(s->layer)
+ , fbo(s->fbo)
+ , invisible(s->invisible)
+ , empty(false)
+ , viewport(s->viewport)
+ , height(s->height)
+ , alpha(s->alpha) {
if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
mTransformRoot.load(*s->transform);
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index d22cb8a..08e9a1a 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -214,18 +214,28 @@ void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t*
int dstY = y + glyph->mBitmapTop;
CacheTexture* cacheTexture = glyph->mCacheTexture;
+ PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer();
+ uint32_t formatSize = PixelBuffer::formatSize(pixelBuffer->getFormat());
uint32_t cacheWidth = cacheTexture->getWidth();
- uint32_t startY = glyph->mStartY * cacheWidth;
- uint32_t endY = startY + (glyph->mBitmapHeight * cacheWidth);
+ uint32_t srcStride = formatSize * cacheWidth;
+ uint32_t startY = glyph->mStartY * srcStride;
+ uint32_t endY = startY + (glyph->mBitmapHeight * srcStride);
- PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer();
const uint8_t* cacheBuffer = pixelBuffer->map();
for (uint32_t cacheY = startY, bitmapY = dstY * bitmapWidth; cacheY < endY;
- cacheY += cacheWidth, bitmapY += bitmapWidth) {
- memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth);
+ cacheY += srcStride, bitmapY += bitmapWidth) {
+
+ if (formatSize == 1) {
+ memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth);
+ } else {
+ for (uint32_t i = 0; i < glyph->mBitmapWidth; ++i) {
+ bitmap[bitmapY + dstX + i] = cacheBuffer[cacheY + (glyph->mStartX + i)*formatSize];
+ }
+ }
}
+
}
void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
diff --git a/libs/hwui/utils/GLUtils.cpp b/libs/hwui/utils/GLUtils.cpp
new file mode 100644
index 0000000..9b298ca
--- /dev/null
+++ b/libs/hwui/utils/GLUtils.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <utils/Log.h>
+
+#include "GLUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+void GLUtils::dumpGLErrors() {
+ GLenum status = GL_NO_ERROR;
+ while ((status = glGetError()) != GL_NO_ERROR) {
+ switch (status) {
+ case GL_INVALID_ENUM:
+ ALOGE("GL error: GL_INVALID_ENUM");
+ break;
+ case GL_INVALID_VALUE:
+ ALOGE("GL error: GL_INVALID_VALUE");
+ break;
+ case GL_INVALID_OPERATION:
+ ALOGE("GL error: GL_INVALID_OPERATION");
+ break;
+ case GL_OUT_OF_MEMORY:
+ ALOGE("GL error: Out of memory!");
+ break;
+ default:
+ ALOGE("GL error: 0x%x", status);
+ }
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/utils/VirtualLightRefBase.h b/libs/hwui/utils/GLUtils.h
index b545aab..890e374 100644
--- a/libs/hwui/utils/VirtualLightRefBase.h
+++ b/libs/hwui/utils/GLUtils.h
@@ -13,22 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef VIRTUALLIGHTREFBASE_H
-#define VIRTUALLIGHTREFBASE_H
-
-#include <utils/RefBase.h>
+#ifndef GLUTILS_H
+#define GLUTILS_H
namespace android {
namespace uirenderer {
-// This is a wrapper around LightRefBase that simply enforces a virtual
-// destructor to eliminate the template requirement of LightRefBase
-class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
+class GLUtils {
+private:
public:
- virtual ~VirtualLightRefBase() {}
-};
+ /**
+ * Print out any GL errors with ALOGE
+ */
+ static void dumpGLErrors();
+
+}; // class GLUtils
} /* namespace uirenderer */
} /* namespace android */
-#endif /* VIRTUALLIGHTREFBASE_H */
+#endif /* GLUTILS_H */
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 7deabe9..8ba44dc 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -38,4 +38,4 @@ public:
} /* namespace uirenderer */
} /* namespace android */
-#endif /* RENDERNODE_H */
+#endif /* MATHUTILS_H */
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
new file mode 100644
index 0000000..bb23a36
--- /dev/null
+++ b/media/java/android/media/AudioAttributes.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A class to encapsulate a collection of attributes describing information about an audio
+ * player or recorder.
+ */
+public final class AudioAttributes {
+ private final static String TAG = "AudioAttributes";
+
+ /**
+ * Content type value to use when the content type is unknown, or other than the ones defined.
+ */
+ public final static int CONTENT_TYPE_UNKNOWN = 0;
+ /**
+ * Content type value to use when the content type is speech.
+ */
+ public final static int CONTENT_TYPE_SPEECH = 1;
+ /**
+ * Content type value to use when the content type is music.
+ */
+ public final static int CONTENT_TYPE_MUSIC = 2;
+ /**
+ * Content type value to use when the content type is a soundtrack, typically accompanying
+ * a movie or TV program.
+ */
+ public final static int CONTENT_TYPE_MOVIE = 3;
+ /**
+ * Content type value to use when the content type is a sound used to accompany a user
+ * action, such as a beep or sound effect expressing a key click, or event, such as the
+ * type of a sound for a bonus being received in a game. These sounds are mostly synthesized
+ * or short Foley sounds.
+ */
+ public final static int CONTENT_TYPE_SONIFICATION = 4;
+
+ /**
+ * Usage value to use when the usage is unknown.
+ */
+ public final static int USAGE_UNKNOWN = 0;
+ /**
+ * Usage value to use when the usage is media, such as music, or movie
+ * soundtracks.
+ */
+ public final static int USAGE_MEDIA = 1;
+ /**
+ * Usage value to use when the usage is voice communications, such as telephony
+ * or VoIP.
+ */
+ public final static int USAGE_VOICE_COMMUNICATION = 2;
+ /**
+ * Usage value to use when the usage is in-call signalling, such as with
+ * a "busy" beep, or DTMF tones.
+ */
+ public final static int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3;
+ /**
+ * Usage value to use when the usage is an alarm (e.g. wake-up alarm).
+ */
+ public final static int USAGE_ALARM = 4;
+ /**
+ * Usage value to use when the usage is notification. See other
+ * notification usages for more specialized uses.
+ */
+ public final static int USAGE_NOTIFICATION = 5;
+ /**
+ * Usage value to use when the usage is telephony ringtone.
+ */
+ public final static int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6;
+ /**
+ * Usage value to use when the usage is a request to enter/end a
+ * communication, such as a VoIP communication or video-conference.
+ */
+ public final static int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7;
+ /**
+ * Usage value to use when the usage is notification for an "instant"
+ * communication such as a chat, or SMS.
+ */
+ public final static int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8;
+ /**
+ * Usage value to use when the usage is notification for a
+ * non-immediate type of communication such as e-mail.
+ */
+ public final static int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9;
+ /**
+ * Usage value to use when the usage is to attract the user's attention,
+ * such as a reminder or low battery warning.
+ */
+ public final static int USAGE_NOTIFICATION_EVENT = 10;
+ /**
+ * Usage value to use when the usage is for accessibility, such as with
+ * a screen reader.
+ */
+ public final static int USAGE_ASSISTANCE_ACCESSIBILITY = 11;
+ /**
+ * Usage value to use when the usage is driving or navigation directions.
+ */
+ public final static int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12;
+ /**
+ * Usage value to use when the usage is sonification, such as with user
+ * interface sounds.
+ */
+ public final static int USAGE_ASSISTANCE_SONIFICATION = 13;
+ /**
+ * Usage value to use when the usage is for game audio.
+ */
+ public final static int USAGE_GAME = 14;
+
+ /**
+ * Flag defining a behavior where the audibility of the sound will be ensured by the system.
+ */
+ public final static int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
+ /**
+ * @hide
+ * Flag defining a behavior where the playback of the sound is ensured without
+ * degradation only when going to a secure sink.
+ */
+ // FIXME not guaranteed yet
+ // TODO add OR to getFlags() when supported and in public API
+ public final static int FLAG_SECURE = 0x1 << 1;
+ /**
+ * @hide
+ * Flag to enable when the stream is associated with SCO usage.
+ * Internal use only for dealing with legacy STREAM_BLUETOOTH_SCO
+ */
+ public final static int FLAG_SCO = 0x1 << 2;
+
+
+ private int mUsage = USAGE_UNKNOWN;
+ private int mContentType = CONTENT_TYPE_UNKNOWN;
+ private int mFlags = 0x0;
+ private HashSet<String> mTags;
+
+ private AudioAttributes() {
+ }
+
+ /**
+ * Return the content type.
+ * @return one of the values that can be set in {@link Builder#setContentType(int)}
+ */
+ public int getContentType() {
+ return mContentType;
+ }
+
+ /**
+ * Return the usage.
+ * @return one of the values that can be set in {@link Builder#setUsage(int)}
+ */
+ public int getUsage() {
+ return mUsage;
+ }
+
+ /**
+ * Return the flags.
+ * @return a combined mask of all flags
+ */
+ public int getFlags() {
+ // only return the flags that are public
+ return (mFlags & (FLAG_AUDIBILITY_ENFORCED));
+ }
+
+ /**
+ * @hide
+ * Return all the flags, even the non-public ones.
+ * Internal use only
+ * @return a combined mask of all flags
+ */
+ public int getAllFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Return the set of tags.
+ * @return a read-only set of all tags stored as strings.
+ */
+ public Set<String> getTags() {
+ return Collections.unmodifiableSet(mTags);
+ }
+
+ /**
+ * Builder class for {@link AudioAttributes} objects.
+ */
+ public static class Builder {
+ private int mUsage = USAGE_UNKNOWN;
+ private int mContentType = CONTENT_TYPE_UNKNOWN;
+ private int mFlags = 0x0;
+ private HashSet<String> mTags = new HashSet<String>();
+
+ /**
+ * Constructs a new Builder with the defaults.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Constructs a new Builder from a given AudioAttributes
+ * @param aa the AudioAttributes object whose data will be reused in the new Builder.
+ */
+ @SuppressWarnings("unchecked") // for cloning of mTags
+ public Builder(AudioAttributes aa) {
+ mUsage = aa.mUsage;
+ mContentType = aa.mContentType;
+ mFlags = aa.mFlags;
+ mTags = (HashSet<String>) aa.mTags.clone();
+ }
+
+ /**
+ * Combines all of the attributes that have been set and return a new
+ * {@link AudioAttributes} object.
+ * @return a new {@link AudioAttributes} object
+ */
+ @SuppressWarnings("unchecked") // for cloning of mTags
+ public AudioAttributes build() {
+ AudioAttributes aa = new AudioAttributes();
+ aa.mContentType = mContentType;
+ aa.mUsage = mUsage;
+ aa.mFlags = mFlags;
+ aa.mTags = (HashSet<String>) mTags.clone();
+ return aa;
+ }
+
+ /**
+ * Sets the attribute describing what is the intended use of the the audio signal,
+ * such as alarm or ringtone.
+ * @param usage one of {@link AudioAttributes#USAGE_UNKNOWN},
+ * {@link AudioAttributes#USAGE_MEDIA},
+ * {@link AudioAttributes#USAGE_VOICE_COMMUNICATION},
+ * {@link AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING},
+ * {@link AudioAttributes#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_TELEPHONY_RINGTONE},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED},
+ * {@link AudioAttributes#USAGE_NOTIFICATION_EVENT},
+ * {@link AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY},
+ * {@link AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE},
+ * {@link AudioAttributes#USAGE_ASSISTANCE_SONIFICATION},
+ * {@link AudioAttributes#USAGE_GAME}.
+ * @return the same Builder instance.
+ */
+ public Builder setUsage(@AttributeUsage int usage) {
+ switch (usage) {
+ case USAGE_UNKNOWN:
+ case USAGE_MEDIA:
+ case USAGE_VOICE_COMMUNICATION:
+ case USAGE_VOICE_COMMUNICATION_SIGNALLING:
+ case USAGE_ALARM:
+ case USAGE_NOTIFICATION:
+ case USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
+ case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+ case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+ case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+ case USAGE_NOTIFICATION_EVENT:
+ case USAGE_ASSISTANCE_ACCESSIBILITY:
+ case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+ case USAGE_ASSISTANCE_SONIFICATION:
+ case USAGE_GAME:
+ mUsage = usage;
+ break;
+ default:
+ mUsage = USAGE_UNKNOWN;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the attribute describing the content type of the audio signal, such as speech,
+ * or music.
+ * @param contentType the content type values, one of
+ * {@link AudioAttributes#CONTENT_TYPE_MOVIE},
+ * {@link AudioAttributes#CONTENT_TYPE_MUSIC},
+ * {@link AudioAttributes#CONTENT_TYPE_SONIFICATION},
+ * {@link AudioAttributes#CONTENT_TYPE_SPEECH},
+ * {@link AudioAttributes#CONTENT_TYPE_UNKNOWN}.
+ * @return the same Builder instance.
+ */
+ public Builder setContentType(@AttributeContentType int contentType) {
+ switch (contentType) {
+ case CONTENT_TYPE_UNKNOWN:
+ case CONTENT_TYPE_MOVIE:
+ case CONTENT_TYPE_MUSIC:
+ case CONTENT_TYPE_SONIFICATION:
+ case CONTENT_TYPE_SPEECH:
+ mContentType = contentType;
+ break;
+ default:
+ mUsage = CONTENT_TYPE_UNKNOWN;
+ }
+ return this;
+ }
+
+ /**
+ * Sets the combination of flags.
+ * @param flags the {@link AudioAttributes#FLAG_AUDIBILITY_ENFORCED} flag.
+ * @return the same Builder instance.
+ */
+ public Builder setFlags(int flags) {
+ flags &= (AudioAttributes.FLAG_AUDIBILITY_ENFORCED | AudioAttributes.FLAG_SCO
+ | AudioAttributes.FLAG_SECURE);
+ mFlags |= flags;
+ return this;
+ }
+
+ /**
+ * Add a custom tag stored as a string
+ * @param tag
+ * @return the same Builder instance.
+ */
+ public Builder addTag(String tag) {
+ mTags.add(tag);
+ return this;
+ }
+
+ /**
+ * Adds attributes inferred from the legacy stream types.
+ * @param streamType one of {@link AudioManager#STREAM_VOICE_CALL},
+ * {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING},
+ * {@link AudioManager#STREAM_MUSIC}, {@link AudioManager#STREAM_ALARM},
+ * or {@link AudioManager#STREAM_NOTIFICATION}.
+ * @return the same Builder instance.
+ */
+ public Builder setLegacyStreamType(int streamType) {
+ return setInternalLegacyStreamType(streamType);
+ }
+
+ /**
+ * @hide
+ * For internal framework use only, enables building from hidden stream types.
+ * @param streamType
+ * @return the same Builder instance.
+ */
+ public Builder setInternalLegacyStreamType(int streamType) {
+ switch(streamType) {
+ case AudioSystem.STREAM_VOICE_CALL:
+ mContentType = CONTENT_TYPE_SPEECH;
+ mUsage = USAGE_VOICE_COMMUNICATION;
+ break;
+ case AudioSystem.STREAM_SYSTEM_ENFORCED:
+ mFlags |= FLAG_AUDIBILITY_ENFORCED;
+ // intended fall through, attributes in common with STREAM_SYSTEM
+ case AudioSystem.STREAM_SYSTEM:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_ASSISTANCE_SONIFICATION;
+ break;
+ case AudioSystem.STREAM_RING:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
+ break;
+ case AudioSystem.STREAM_MUSIC:
+ mContentType = CONTENT_TYPE_MUSIC;
+ mUsage = USAGE_MEDIA;
+ break;
+ case AudioSystem.STREAM_ALARM:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_ALARM;
+ break;
+ case AudioSystem.STREAM_NOTIFICATION:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_NOTIFICATION;
+ break;
+ case AudioSystem.STREAM_BLUETOOTH_SCO:
+ mContentType = CONTENT_TYPE_SPEECH;
+ mUsage = USAGE_VOICE_COMMUNICATION;
+ mFlags |= FLAG_SCO;
+ break;
+ case AudioSystem.STREAM_DTMF:
+ mContentType = CONTENT_TYPE_SONIFICATION;
+ mUsage = USAGE_VOICE_COMMUNICATION_SIGNALLING;
+ break;
+ case AudioSystem.STREAM_TTS:
+ mContentType = CONTENT_TYPE_SPEECH;
+ mUsage = USAGE_ASSISTANCE_ACCESSIBILITY;
+ break;
+ default:
+ Log.e(TAG, "Invalid stream type " + streamType + " in for AudioAttributes");
+ }
+ return this;
+ }
+ };
+
+ /** @hide */
+ @Override
+ public String toString () {
+ return new String("AudioAttributes:"
+ + " usage=" + mUsage
+ + " content=" + mContentType
+ + " flags=0x" + Integer.toHexString(mFlags)
+ + " tags=" + mTags);
+ }
+
+ /** @hide */
+ @IntDef({
+ USAGE_UNKNOWN,
+ USAGE_MEDIA,
+ USAGE_VOICE_COMMUNICATION,
+ USAGE_VOICE_COMMUNICATION_SIGNALLING,
+ USAGE_ALARM,
+ USAGE_NOTIFICATION,
+ USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
+ USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
+ USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
+ USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
+ USAGE_NOTIFICATION_EVENT,
+ USAGE_ASSISTANCE_ACCESSIBILITY,
+ USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+ USAGE_ASSISTANCE_SONIFICATION,
+ USAGE_GAME
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AttributeUsage {}
+
+ /** @hide */
+ @IntDef({
+ CONTENT_TYPE_UNKNOWN,
+ CONTENT_TYPE_SPEECH,
+ CONTENT_TYPE_MUSIC,
+ CONTENT_TYPE_MOVIE,
+ CONTENT_TYPE_SONIFICATION
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AttributeContentType {}
+}
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 6b2a247..57274ee 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -37,7 +37,7 @@ public class AudioFormat {
public static final int ENCODING_PCM_16BIT = 2;
/** Audio data format: PCM 8 bit per sample. Not guaranteed to be supported by devices. */
public static final int ENCODING_PCM_8BIT = 3;
- /** @hide Candidate for public API */
+ /** Audio data format: single-precision floating-point per sample */
public static final int ENCODING_PCM_FLOAT = 4;
/** Invalid audio channel configuration */
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 0c8a823..724022b 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -149,6 +149,7 @@ public class AudioService extends IAudioService.Stub {
private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 18;
private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 19;
private static final int MSG_UNLOAD_SOUND_EFFECTS = 20;
+ private static final int MSG_SYSTEM_READY = 21;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -370,7 +371,7 @@ public class AudioService extends IAudioService.Stub {
private int mScoConnectionState;
// true if boot sequence has been completed
- private boolean mBootCompleted;
+ private boolean mSystemReady;
// listener for SoundPool sample load completion indication
private SoundPoolCallback mSoundPoolCallBack;
// thread for SoundPool listener
@@ -525,7 +526,6 @@ public class AudioService extends IAudioService.Stub {
intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG);
- intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
@@ -559,6 +559,43 @@ public class AudioService extends IAudioService.Stub {
}
+ public void systemReady() {
+ sendMsg(mAudioHandler, MSG_SYSTEM_READY, SENDMSG_QUEUE,
+ 0, 0, null, 0);
+ }
+
+ public void onSystemReady() {
+ mSystemReady = true;
+ sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE,
+ 0, 0, null, 0);
+
+ mKeyguardManager =
+ (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
+ resetBluetoothSco();
+ getBluetoothHeadset();
+ //FIXME: this is to maintain compatibility with deprecated intent
+ // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
+ Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
+ newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ sendStickyBroadcastToAll(newIntent);
+
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+ BluetoothProfile.A2DP);
+ }
+
+ sendMsg(mAudioHandler,
+ MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED,
+ SENDMSG_REPLACE,
+ 0,
+ 0,
+ null,
+ SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
+ }
+
private void createAudioSystemThread() {
mAudioSystemThread = new AudioSystemThread();
mAudioSystemThread.start();
@@ -1996,7 +2033,7 @@ public class AudioService extends IAudioService.Stub {
/** @see AudioManager#startBluetoothSco() */
public void startBluetoothSco(IBinder cb, int targetSdkVersion){
if (!checkAudioSettingsPermission("startBluetoothSco()") ||
- !mBootCompleted) {
+ !mSystemReady) {
return;
}
ScoClient client = getScoClient(cb, true);
@@ -2013,7 +2050,7 @@ public class AudioService extends IAudioService.Stub {
/** @see AudioManager#stopBluetoothSco() */
public void stopBluetoothSco(IBinder cb){
if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
- !mBootCompleted) {
+ !mSystemReady) {
return;
}
ScoClient client = getScoClient(cb, false);
@@ -3277,7 +3314,7 @@ public class AudioService extends IAudioService.Stub {
int status;
synchronized (mSoundEffectsLock) {
- if (!mBootCompleted) {
+ if (!mSystemReady) {
Log.w(TAG, "onLoadSoundEffects() called before boot complete");
return false;
}
@@ -3700,6 +3737,10 @@ public class AudioService extends IAudioService.Stub {
case MSG_BROADCAST_BT_CONNECTION_STATE:
onBroadcastScoConnectionState(msg.arg1);
break;
+
+ case MSG_SYSTEM_READY:
+ onSystemReady();
+ break;
}
}
}
@@ -4169,36 +4210,6 @@ public class AudioService extends IAudioService.Stub {
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
sendStickyBroadcastToAll(newIntent);
}
- } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
- mBootCompleted = true;
- sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE,
- 0, 0, null, 0);
-
- mKeyguardManager =
- (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
- mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
- resetBluetoothSco();
- getBluetoothHeadset();
- //FIXME: this is to maintain compatibility with deprecated intent
- // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
- Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
- newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
- AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- sendStickyBroadcastToAll(newIntent);
-
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null) {
- adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
- BluetoothProfile.A2DP);
- }
-
- sendMsg(mAudioHandler,
- MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED,
- SENDMSG_REPLACE,
- 0,
- 0,
- null,
- SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
AudioSystem.setParameters("screen_state=on");
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 007eb40..1a64cff 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -42,7 +42,8 @@ import com.android.internal.app.IAppOpsService;
* The AudioTrack class manages and plays a single audio resource for Java applications.
* It allows streaming of PCM audio buffers to the audio sink for playback. This is
* achieved by "pushing" the data to the AudioTrack object using one of the
- * {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods.
+ * {@link #write(byte[], int, int)}, {@link #write(short[], int, int)},
+ * and {@link #write(float[], int, int, int)} methods.
*
* <p>An AudioTrack instance can operate under two modes: static or streaming.<br>
* In Streaming mode, the application writes a continuous stream of data to the AudioTrack, using
@@ -244,6 +245,7 @@ public class AudioTrack
* The encoding of the audio samples.
* @see AudioFormat#ENCODING_PCM_8BIT
* @see AudioFormat#ENCODING_PCM_16BIT
+ * @see AudioFormat#ENCODING_PCM_FLOAT
*/
private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
/**
@@ -285,8 +287,9 @@ public class AudioTrack
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
* @param audioFormat the format in which the audio data is represented.
- * See {@link AudioFormat#ENCODING_PCM_16BIT} and
- * {@link AudioFormat#ENCODING_PCM_8BIT}
+ * See {@link AudioFormat#ENCODING_PCM_16BIT},
+ * {@link AudioFormat#ENCODING_PCM_8BIT},
+ * and {@link AudioFormat#ENCODING_PCM_FLOAT}.
* @param bufferSizeInBytes the total size (in bytes) of the internal buffer where audio data is
* read from for playback.
* If track's creation mode is {@link #MODE_STREAM}, you can write data into
@@ -329,7 +332,8 @@ public class AudioTrack
* {@link AudioFormat#CHANNEL_OUT_STEREO}
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT} and
- * {@link AudioFormat#ENCODING_PCM_8BIT}
+ * {@link AudioFormat#ENCODING_PCM_8BIT},
+ * and {@link AudioFormat#ENCODING_PCM_FLOAT}.
* @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is read
* from for playback. If using the AudioTrack in streaming mode, you can write data into
* this buffer in smaller chunks than this size. If using the AudioTrack in static mode,
@@ -459,11 +463,14 @@ public class AudioTrack
break;
case AudioFormat.ENCODING_PCM_16BIT:
case AudioFormat.ENCODING_PCM_8BIT:
+ case AudioFormat.ENCODING_PCM_FLOAT:
mAudioFormat = audioFormat;
break;
default:
throw new IllegalArgumentException("Unsupported sample encoding."
- + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT.");
+ + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT"
+ + " or ENCODING_PCM_FLOAT"
+ + ".");
}
//--------------
@@ -723,7 +730,8 @@ public class AudioTrack
* {@link AudioFormat#CHANNEL_OUT_STEREO}
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT} and
- * {@link AudioFormat#ENCODING_PCM_8BIT}
+ * {@link AudioFormat#ENCODING_PCM_8BIT},
+ * and {@link AudioFormat#ENCODING_PCM_FLOAT}.
* @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed,
* or {@link #ERROR} if unable to query for output properties,
* or the minimum buffer size expressed in bytes.
@@ -750,7 +758,8 @@ public class AudioTrack
}
if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT)
- && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) {
+ && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)
+ && (audioFormat != AudioFormat.ENCODING_PCM_FLOAT)) {
loge("getMinBufferSize(): Invalid audio format.");
return ERROR_BAD_VALUE;
}
@@ -1150,7 +1159,7 @@ public class AudioTrack
public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) {
- if (mState == STATE_UNINITIALIZED) {
+ if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
return ERROR_INVALID_OPERATION;
}
@@ -1188,13 +1197,13 @@ public class AudioTrack
* starts.
* @param sizeInShorts the number of shorts to read in audioData after the offset.
* @return the number of shorts that were written or {@link #ERROR_INVALID_OPERATION}
- * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
- * the parameters don't resolve to valid data and indexes.
+ * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+ * the parameters don't resolve to valid data and indexes.
*/
public int write(short[] audioData, int offsetInShorts, int sizeInShorts) {
- if (mState == STATE_UNINITIALIZED) {
+ if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
return ERROR_INVALID_OPERATION;
}
@@ -1220,6 +1229,79 @@ public class AudioTrack
/**
* Writes the audio data to the audio sink for playback (streaming mode),
* or copies audio data for later playback (static buffer mode).
+ * In static buffer mode, copies the data to the buffer starting at offset 0,
+ * and the write mode is ignored.
+ * In streaming mode, the blocking behavior will depend on the write mode.
+ * <p>
+ * Note that the actual playback of this data might occur after this function
+ * returns. This function is thread safe with respect to {@link #stop} calls,
+ * in which case all of the specified data might not be written to the audio sink.
+ * <p>
+ * @param audioData the array that holds the data to play.
+ * The implementation does not clip for sample values within the nominal range
+ * [-1.0f, 1.0f], provided that all gains in the audio pipeline are
+ * less than or equal to unity (1.0f), and in the absence of post-processing effects
+ * that could add energy, such as reverb. For the convenience of applications
+ * that compute samples using filters with non-unity gain,
+ * sample values +3 dB beyond the nominal range are permitted.
+ * However such values may eventually be limited or clipped, depending on various gains
+ * and later processing in the audio path. Therefore applications are encouraged
+ * to provide samples values within the nominal range.
+ * @param offsetInFloats the offset, expressed as a number of floats,
+ * in audioData where the data to play starts.
+ * @param sizeInFloats the number of floats to read in audioData after the offset.
+ * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
+ * effect in static mode.
+ * <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
+ * to the audio sink.
+ * <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
+ * queuing as much audio data for playback as possible without blocking.
+ * @return the number of floats that were written, or {@link #ERROR_INVALID_OPERATION}
+ * if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
+ * the parameters don't resolve to valid data and indexes.
+ */
+ public int write(float[] audioData, int offsetInFloats, int sizeInFloats,
+ @WriteMode int writeMode) {
+
+ if (mState == STATE_UNINITIALIZED) {
+ Log.e(TAG, "AudioTrack.write() called in invalid state STATE_UNINITIALIZED");
+ return ERROR_INVALID_OPERATION;
+ }
+
+ if (mAudioFormat != AudioFormat.ENCODING_PCM_FLOAT) {
+ Log.e(TAG, "AudioTrack.write(float[] ...) requires format ENCODING_PCM_FLOAT");
+ return ERROR_INVALID_OPERATION;
+ }
+
+ if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) {
+ Log.e(TAG, "AudioTrack.write() called with invalid blocking mode");
+ return ERROR_BAD_VALUE;
+ }
+
+ if ( (audioData == null) || (offsetInFloats < 0 ) || (sizeInFloats < 0)
+ || (offsetInFloats + sizeInFloats < 0) // detect integer overflow
+ || (offsetInFloats + sizeInFloats > audioData.length)) {
+ Log.e(TAG, "AudioTrack.write() called with invalid array, offset, or size");
+ return ERROR_BAD_VALUE;
+ }
+
+ int ret = native_write_float(audioData, offsetInFloats, sizeInFloats, mAudioFormat,
+ writeMode == WRITE_BLOCKING);
+
+ if ((mDataLoadMode == MODE_STATIC)
+ && (mState == STATE_NO_STATIC_DATA)
+ && (ret > 0)) {
+ // benign race with respect to other APIs that read mState
+ mState = STATE_INITIALIZED;
+ }
+
+ return ret;
+ }
+
+
+ /**
+ * Writes the audio data to the audio sink for playback (streaming mode),
+ * or copies audio data for later playback (static buffer mode).
* In static buffer mode, copies the data to the buffer starting at its 0 offset, and the write
* mode is ignored.
* In streaming mode, the blocking behavior will depend on the write mode.
@@ -1247,6 +1329,11 @@ public class AudioTrack
return ERROR_INVALID_OPERATION;
}
+ if (mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
+ Log.e(TAG, "AudioTrack.write(ByteBuffer ...) not yet supported for ENCODING_PCM_FLOAT");
+ return ERROR_INVALID_OPERATION;
+ }
+
if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) {
Log.e(TAG, "AudioTrack.write() called with invalid blocking mode");
return ERROR_BAD_VALUE;
@@ -1487,6 +1574,10 @@ public class AudioTrack
private native final int native_write_short(short[] audioData,
int offsetInShorts, int sizeInShorts, int format);
+ private native final int native_write_float(float[] audioData,
+ int offsetInFloats, int sizeInFloats, int format,
+ boolean isBlocking);
+
private native final int native_write_native_bytes(Object audioData,
int positionInBytes, int sizeInBytes, int format, boolean blocking);
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 115786c..34c5520 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -585,11 +585,63 @@ final public class MediaCodec {
* the codec. If you previously specified a surface when configuring this
* video decoder you can optionally render the buffer.
* @param index The index of a client-owned output buffer previously returned
- * in a call to {@link #dequeueOutputBuffer}.
+ * from a call to {@link #dequeueOutputBuffer}.
* @param render If a valid surface was specified when configuring the codec,
* passing true renders this output buffer to the surface.
*/
- public native final void releaseOutputBuffer(int index, boolean render);
+ public final void releaseOutputBuffer(int index, boolean render) {
+ releaseOutputBuffer(index, render, false /* updatePTS */, 0 /* dummy */);
+ }
+
+ /**
+ * If you are done with a buffer, use this call to update its surface timestamp
+ * and return it to the codec to render it on the output surface. If you
+ * have not specified an output surface when configuring this video codec,
+ * this call will simply return the buffer to the codec.<p>
+ *
+ * The timestamp may have special meaning depending on the destination surface.
+ *
+ * <table>
+ * <tr><th>SurfaceView specifics</th></tr>
+ * <tr><td>
+ * If you render your buffer on a {@link android.view.SurfaceView},
+ * you can use the timestamp to render the buffer at a specific time (at the
+ * VSYNC at or after the buffer timestamp). For this to work, the timestamp
+ * needs to be <i>reasonably close</i> to the current {@link System#nanoTime}.
+ * Currently, this is set as within one (1) second. A few notes:
+ *
+ * <ul>
+ * <li>the buffer will not be returned to the codec until the timestamp
+ * has passed and the buffer is no longer used by the {@link android.view.Surface}.
+ * <li>buffers are processed sequentially, so you may block subsequent buffers to
+ * be displayed on the {@link android.view.Surface}. This is important if you
+ * want to react to user action, e.g. stop the video or seek.
+ * <li>if multiple buffers are sent to the {@link android.view.Surface} to be
+ * rendered at the same VSYNC, the last one will be shown, and the other ones
+ * will be dropped.
+ * <li>if the timestamp is <em>not</em> "reasonably close" to the current system
+ * time, the {@link android.view.Surface} will ignore the timestamp, and
+ * display the buffer at the earliest feasible time. In this mode it will not
+ * drop frames.
+ * <li>for best performance and quality, call this method when you are about
+ * two VSYNCs' time before the desired render time. For 60Hz displays, this is
+ * about 33 msec.
+ * </ul>
+ * </td></tr>
+ * </table>
+ *
+ * @param index The index of a client-owned output buffer previously returned
+ * from a call to {@link #dequeueOutputBuffer}.
+ * @param renderTimestampNs The timestamp to associate with this buffer when
+ * it is sent to the Surface.
+ */
+ public final void releaseOutputBuffer(int index, long renderTimestampNs) {
+ releaseOutputBuffer(
+ index, true /* render */, true /* updatePTS */, renderTimestampNs);
+ }
+
+ private native final void releaseOutputBuffer(
+ int index, boolean render, boolean updatePTS, long timeNs);
/**
* Signals end-of-stream on input. Equivalent to submitting an empty buffer with
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index a710c03..4a7c096 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -260,7 +260,11 @@ status_t JMediaCodec::dequeueOutputBuffer(
return OK;
}
-status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) {
+status_t JMediaCodec::releaseOutputBuffer(
+ size_t index, bool render, bool updatePTS, int64_t timestampNs) {
+ if (updatePTS) {
+ return mCodec->renderOutputBufferAndRelease(index, timestampNs);
+ }
return render
? mCodec->renderOutputBufferAndRelease(index)
: mCodec->releaseOutputBuffer(index);
@@ -873,7 +877,8 @@ static jint android_media_MediaCodec_dequeueOutputBuffer(
}
static void android_media_MediaCodec_releaseOutputBuffer(
- JNIEnv *env, jobject thiz, jint index, jboolean render) {
+ JNIEnv *env, jobject thiz,
+ jint index, jboolean render, jboolean updatePTS, jlong timestampNs) {
ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
@@ -883,7 +888,7 @@ static void android_media_MediaCodec_releaseOutputBuffer(
return;
}
- status_t err = codec->releaseOutputBuffer(index, render);
+ status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs);
throwExceptionAsNecessary(env, err);
}
@@ -1138,7 +1143,7 @@ static JNINativeMethod gMethods[] = {
{ "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
(void *)android_media_MediaCodec_dequeueOutputBuffer },
- { "releaseOutputBuffer", "(IZ)V",
+ { "releaseOutputBuffer", "(IZZJ)V",
(void *)android_media_MediaCodec_releaseOutputBuffer },
{ "signalEndOfInputStream", "()V",
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 2f2ea96..bf9f4ea 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -79,7 +79,8 @@ struct JMediaCodec : public AHandler {
status_t dequeueOutputBuffer(
JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs);
- status_t releaseOutputBuffer(size_t index, bool render);
+ status_t releaseOutputBuffer(
+ size_t index, bool render, bool updatePTS, int64_t timestampNs);
status_t signalEndOfInputStream();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index b2b2bd8..9069a55 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -436,6 +436,8 @@ public class DirectoryFragment extends Fragment {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
+ mode.setTitle(getResources()
+ .getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount()));
return true;
}
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
index 546ddd4..0d943ed 100644
--- a/packages/Keyguard/res/layout/keyguard_status_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -28,7 +28,7 @@
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
androidprv:layout_maxHeight="@dimen/keyguard_security_height"
android:gravity="center_horizontal|top"
- android:layout_marginTop="32dp"
+ android:layout_marginTop="48dp"
android:layout_marginBottom="32dp"
android:contentDescription="@string/keyguard_accessibility_status">
<LinearLayout
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.png
deleted file mode 100644
index 54dde82..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_open_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_open_normal.png
deleted file mode 100644
index 3c0dc4e..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_notify_open_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png
deleted file mode 100644
index 3b1944d..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png
deleted file mode 100644
index 693abf5..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_notify_clear_normal.png
deleted file mode 100644
index c526433..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_notify_clear_normal.png
deleted file mode 100644
index d13bc69..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_notify_clear_normal.png
deleted file mode 100644
index a137a80..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_notify_clear_normal.png
deleted file mode 100644
index 8da7945..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.png
deleted file mode 100644
index 7cb52e3..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_open_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_open_normal.png
deleted file mode 100644
index 8010ce7..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_notify_open_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png
deleted file mode 100644
index 807f607..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png
deleted file mode 100644
index 15340d3..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.png
deleted file mode 100644
index b9afa44..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_open_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_open_normal.png
deleted file mode 100644
index 6d46fdd..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_open_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png
deleted file mode 100644
index e562bc2..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png
deleted file mode 100644
index e3cc9b0..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_clear_normal.png
deleted file mode 100644
index afdee8f..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_open_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_open_normal.png
deleted file mode 100644
index 7742207..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_open_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.png
deleted file mode 100644
index a2e8fe1..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png
deleted file mode 100644
index e15981a..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_settings_24dp.xml b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
new file mode 100644
index 0000000..5c38a22
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
@@ -0,0 +1,29 @@
+<!-- Copyright (C) 2014 The Android Open Source Project
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at"+
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT 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="24.0"
+ android:viewportHeight="24.0"/>
+
+<group>
+<path
+ android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z"
+ android:fill="#ffffffff"
+ />
+</group>
+</vector>
diff --git a/packages/SystemUI/res/layout/status_bar_flip_button.xml b/packages/SystemUI/res/drawable/notification_header_bg.xml
index f4d7033..09d0d7d 100644
--- a/packages/SystemUI/res/layout/status_bar_flip_button.xml
+++ b/packages/SystemUI/res/drawable/notification_header_bg.xml
@@ -15,11 +15,17 @@
~ limitations under the License
-->
-<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/settings_button"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:scaleType="center"
- android:src="@drawable/ic_notify_quicksettings"
- android:background="@drawable/ic_notify_button_bg"
- android:contentDescription="@string/accessibility_desc_quick_settings"/> \ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <shape>
+ <solid android:color="@color/background_color_1_press" />
+ <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
+ </shape>
+ </item>
+ <item>
+ <shape>
+ <solid android:color="@color/background_color_1" />
+ <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
+ </shape>
+ </item>
+</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
new file mode 100644
index 0000000..c015cc8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_dismiss_dark.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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:versionCode="1" >
+
+ <size
+ android:height="16dp"
+ android:width="16dp" />
+
+ <viewport
+ android:viewportHeight="100"
+ android:viewportWidth="100" />
+
+ <group>
+ <path
+ android:name="x"
+ android:pathData="M0,0L100,100M0,100L100,0z"
+ android:stroke="@color/recents_task_bar_dark_dismiss_color"
+ android:strokeWidth="8.0"
+ android:strokeLineCap="square" />
+ </group>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_light.xml b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
new file mode 100644
index 0000000..9c93db9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_dismiss_light.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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:versionCode="1" >
+
+ <size
+ android:height="16dp"
+ android:width="16dp" />
+
+ <viewport
+ android:viewportHeight="100"
+ android:viewportWidth="100" />
+
+ <group>
+ <path
+ android:name="x"
+ android:pathData="M0,0L100,100M0,100L100,0z"
+ android:stroke="@color/recents_task_bar_light_dismiss_color"
+ android:strokeWidth="8.0"
+ android:strokeLineCap="square" />
+ </group>
+</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/heads_up.xml b/packages/SystemUI/res/layout/heads_up.xml
index e4954e7..7d9cfa1 100644
--- a/packages/SystemUI/res/layout/heads_up.xml
+++ b/packages/SystemUI/res/layout/heads_up.xml
@@ -20,7 +20,7 @@
<com.android.systemui.statusbar.policy.HeadsUpNotificationView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
- android:layout_width="@dimen/notification_panel_width"
+ android:layout_width="match_parent"
android:id="@+id/content_holder"
android:background="@drawable/notification_panel_bg"
/> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index b7df51d..1efda8c 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -29,7 +29,7 @@
<com.android.systemui.settings.ToggleSlider
android:id="@+id/brightness_slider"
android:layout_width="0dp"
- android:layout_height="40dp"
+ android:layout_height="44dp"
android:layout_gravity="center_vertical"
android:layout_weight="1"
systemui:text="@string/status_bar_settings_auto_brightness_label" />
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index f7df18eb..bda6431 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -63,6 +63,13 @@
android:maxLines="2"
android:ellipsize="marquee"
android:fadingEdge="horizontal" />
+ <ImageView
+ android:id="@+id/dismiss_task"
+ android:layout_width="@dimen/recents_task_view_application_icon_size"
+ android:layout_height="@dimen/recents_task_view_application_icon_size"
+ android:layout_gravity="center_vertical|end"
+ android:padding="23dp"
+ android:src="@drawable/recents_dismiss_dark" />
</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 1b35537..585658e 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -77,26 +77,24 @@
<LinearLayout android:id="@+id/system_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:orientation="horizontal">
-
- <LinearLayout android:id="@+id/statusIcons"
+ android:orientation="horizontal"
+ >
+ <LinearLayout android:id="@+id/system_icons"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
- android:orientation="horizontal"/>
-
- <LinearLayout
- android:id="@+id/signal_battery_cluster"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:paddingStart="2dp"
- android:orientation="horizontal"
- android:gravity="center"
>
- <include layout="@layout/signal_cluster_view"
+ <LinearLayout android:id="@+id/statusIcons"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"/>
+
+ <include layout="@layout/signal_cluster_view"
android:id="@+id/signal_cluster"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginStart="2dp"
/>
<!-- battery must be padded below to match assets -->
<com.android.systemui.BatteryMeterView
@@ -107,7 +105,6 @@
android:layout_marginStart="4dip"
/>
</LinearLayout>
-
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 3267c36..f045da4 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -22,7 +22,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:id="@+id/notification_panel"
- android:layout_width="0dp"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
>
@@ -34,15 +34,6 @@
android:layout_gravity="bottom"
/>
- <com.android.keyguard.CarrierText
- android:id="@+id/keyguard_carrier_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="2dp"
- android:layout_marginLeft="8dp"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
<include
layout="@layout/keyguard_status_view"
android:layout_height="wrap_content"
@@ -59,9 +50,8 @@
/>
<com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
+ style="@style/NotificationsQuickSettings"
android:id="@+id/notification_container_parent"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
android:clipToPadding="false"
android:clipChildren="false">
@@ -98,11 +88,7 @@
</com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
-
- <include layout="@layout/status_bar_expanded_header"
- android:layout_width="match_parent"
- android:layout_height="@dimen/status_bar_header_height"
- />
+ <include layout="@layout/status_bar_expanded_header" />
<include
layout="@layout/keyguard_bottom_area"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 460dd4b..89fa988 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -20,11 +20,12 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:id="@+id/header"
- android:layout_width="match_parent"
+ style="@style/StatusBarHeader"
android:layout_height="@dimen/status_bar_header_height"
- android:orientation="horizontal"
- android:gravity="center_vertical"
+ android:paddingStart="@dimen/notification_side_padding"
+ android:paddingEnd="@dimen/notification_side_padding"
android:baselineAligned="false"
+ android:elevation="10dp"
>
<View
@@ -37,10 +38,12 @@
<RelativeLayout
android:id="@+id/datetime"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_gravity="start"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="16dp"
android:background="@drawable/ic_notify_button_bg"
android:enabled="false"
>
@@ -48,10 +51,9 @@
android:id="@+id/clock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginEnd="8dp"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
- android:layout_centerVertical="true"
+ systemui:amPmStyle="normal"
/>
<com.android.systemui.statusbar.policy.DateView android:id="@+id/date"
@@ -59,11 +61,49 @@
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
- android:layout_toEndOf="@id/clock"
- android:layout_alignBaseline="@id/clock"
+ android:layout_below="@id/clock"
/>
</RelativeLayout>
+ <com.android.keyguard.CarrierText
+ android:id="@+id/keyguard_carrier_text"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/status_bar_header_height_keyguard"
+ android:layout_marginLeft="8dp"
+ android:gravity="center_vertical"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <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="6dp"
+ />
+
+ <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"
+ />
+
+ <include
+ layout="@layout/quick_settings_brightness_dialog"
+ android:id="@+id/brightness_container"
+ android:layout_width="match_parent"
+ />
+
<TextView
android:id="@+id/header_debug_info"
android:visibility="invisible"
@@ -77,22 +117,4 @@
android:padding="2dp"
/>
- <include layout="@layout/status_bar_flip_button"
- android:id="@+id/header_flipper"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:layout_alignParentEnd="true"/>
-
- <ImageView android:id="@+id/clear_all_button"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:layout_toStartOf="@id/header_flipper"
- android:scaleType="center"
- android:src="@drawable/ic_notify_clear"
- android:background="@drawable/ic_notify_button_bg"
- android:contentDescription="@string/accessibility_clear_all"
- />
-
-
-
</com.android.systemui.statusbar.phone.StatusBarHeaderView>
diff --git a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
index e6d7c93..0e84762 100644
--- a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
+++ b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
@@ -28,6 +28,7 @@
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:button="@null"
+ android:background="@*android:drawable/switch_track_quantum"
/>
<com.android.systemui.settings.ToggleSeekBar
android:id="@+id/slider"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index f9b022c..26616cd 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -35,9 +35,9 @@
android:layout_width="match_parent"
android:layout_height="match_parent" >
<include layout="@layout/status_bar_expanded"
- android:layout_width="@dimen/notification_panel_width"
+ android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="start|top" />
+ android:visibility="gone" />
</com.android.systemui.statusbar.phone.PanelHolder>
</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/SystemUI/res/layout/user_switcher_host.xml b/packages/SystemUI/res/layout/user_switcher_host.xml
index bc56cf6..70c5042 100644
--- a/packages/SystemUI/res/layout/user_switcher_host.xml
+++ b/packages/SystemUI/res/layout/user_switcher_host.xml
@@ -22,7 +22,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="#dd000000">
+ android:background="#dd000000"
+ android:elevation="12dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index b7becac..d4a9986 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -18,4 +18,15 @@
<style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
<item name="android:layout_width">480dp</item>
</style>
+
+ <style name="NotificationsQuickSettings">
+ <item name="android:layout_width">@dimen/notification_panel_width</item>
+ <item name="android:layout_height">match_parent</item>
+ <item name="android:layout_gravity">top|center_horizontal</item>
+ </style>
+
+ <style name="StatusBarHeader">
+ <item name="android:layout_width">@dimen/notification_panel_width</item>
+ <item name="android:layout_gravity">center_horizontal</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index f5674d2..8fd1206 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -51,6 +51,13 @@
<enum name="end" value="1" />
</attr>
</declare-styleable>
+ <declare-styleable name="Clock">
+ <attr name="amPmStyle" format="enum">
+ <enum name="normal" value="0" />
+ <enum name="small" value="1" />
+ <enum name="gone" value="2" />
+ </attr>
+ </declare-styleable>
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 9281265..c1a4e26 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -30,7 +30,6 @@
<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>
- <drawable name="notification_header_bg">#FF000000</drawable>
<color name="notification_panel_scrim_color">#A0000000</color>
<color name="notification_panel_scrim_color_keyguard">#80000000</color>
<color name="batterymeter_frame_color">#66FFFFFF</color><!-- 40% white -->
@@ -53,10 +52,18 @@
<!-- The default recents task bar background color. -->
<color name="recents_task_bar_default_background_color">#e6444444</color>
<!-- The default recents task bar text color. -->
- <color name="recents_task_bar_default_text_color">#ffffffff</color>
+ <color name="recents_task_bar_default_text_color">#ffeeeeee</color>
<!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
- <color name="recents_task_bar_light_text_color">#ffffffff</color>
+ <color name="recents_task_bar_light_text_color">#ffeeeeee</color>
<!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
<color name="recents_task_bar_dark_text_color">#ff222222</color>
+ <!-- The recents task bar light dismiss icon color to be drawn on top of dark backgrounds. -->
+ <color name="recents_task_bar_light_dismiss_color">#ffeeeeee</color>
+ <!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
+ <color name="recents_task_bar_dark_dismiss_color">#ff333333</color>
+ <!-- Our quantum 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>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c0376f0..21eb41c 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -119,7 +119,7 @@
<!-- The animation duration for animating in the info pane. -->
<integer name="recents_animate_task_view_info_pane_duration">150</integer>
<!-- The animation duration for animating the removal of a task view. -->
- <integer name="recents_animate_task_view_remove_duration">150</integer>
+ <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 -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c6fdc16..ab34030 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -153,11 +153,14 @@
<dimen name="close_handle_underlap">32dp</dimen>
<!-- Height of the status bar header bar -->
- <dimen name="status_bar_header_height">48dp</dimen>
+ <dimen name="status_bar_header_height">56dp</dimen>
<!-- Height of the status bar header bar when expanded -->
<dimen name="status_bar_header_height_expanded">144dp</dimen>
+ <!-- Height of the status bar header bar when on Keyguard -->
+ <dimen name="status_bar_header_height_keyguard">40dp</dimen>
+
<!-- Gravity for the notification panel -->
<!-- 0x37 = fill_horizontal|top -->
<integer name="notification_panel_layout_gravity">0x37</integer>
@@ -196,9 +199,6 @@
<!-- Quick Settings CA Cert Warning tile geometry: gap between icon and text -->
<dimen name="qs_cawarn_tile_margin_below_icon">3dp</dimen>
- <!-- The width of the notification panel window: match_parent below sw600dp -->
- <dimen name="notification_panel_width">-1dp</dimen>
-
<!-- used by DessertCase -->
<dimen name="dessert_case_cell_size">192dp</dimen>
@@ -224,7 +224,7 @@
<dimen name="recents_task_view_z_increment">5dp</dimen>
<!-- The amount to translate when animating the removal of a task. -->
- <dimen name="recents_task_view_remove_anim_translation_x">75dp</dimen>
+ <dimen name="recents_task_view_remove_anim_translation_x">100dp</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>
@@ -243,7 +243,10 @@
<dimen name="top_stack_peek_amount">12dp</dimen>
<!-- Space reserved for the cards behind the top card in the bottom stack -->
- <dimen name="bottom_stack_peek_amount">18dp</dimen>
+ <dimen name="bottom_stack_peek_amount">12dp</dimen>
+
+ <!-- The height of the area before the bottom stack in which the notifications slow down -->
+ <dimen name="bottom_stack_slow_down_length">12dp</dimen>
<!-- The side padding of the notifications-->
<dimen name="notification_side_padding">8dp</dimen>
@@ -251,8 +254,11 @@
<!-- Z distance between notifications if they are in the stack -->
<dimen name="z_distance_between_notifications">2dp</dimen>
+ <!-- The padding between the individual notification cards when dimmed. -->
+ <dimen name="notification_padding_dimmed">0dp</dimen>
+
<!-- The padding between the individual notification cards. -->
- <dimen name="notification_padding">3dp</dimen>
+ <dimen name="notification_padding">4dp</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>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8ab646d..4f52870 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -69,8 +69,7 @@
<style name="TextAppearance.StatusBar.Expanded" parent="@*android:style/TextAppearance.StatusBar" />
<style name="TextAppearance.StatusBar.Expanded.Clock">
- <item name="android:textSize">32dp</item>
- <item name="android:fontFamily">sans-serif-light</item>
+ <item name="android:textSize">18dp</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">#ffffff</item>
</style>
@@ -78,8 +77,7 @@
<style name="TextAppearance.StatusBar.Expanded.Date">
<item name="android:textSize">12dp</item>
<item name="android:textStyle">normal</item>
- <item name="android:textColor">#cccccc</item>
- <item name="android:textAllCaps">true</item>
+ <item name="android:textColor">#afb3b6</item>
</style>
<style name="TextAppearance.StatusBar.Expanded.Network" parent="@style/TextAppearance.StatusBar.Expanded.Date">
@@ -138,6 +136,7 @@
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:padding">16dp</item>
+ <item name="android:layout_alignParentBottom">true</item>
</style>
<style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer" />
@@ -169,6 +168,16 @@
<item name="android:textSize">14dp</item>
</style>
- <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault" />
+ <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:colorPrimary">@color/primary_color</item>
+ </style>
+ <style name="NotificationsQuickSettings">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">match_parent</item>
+ </style>
+
+ <style name="StatusBarHeader">
+ <item name="android:layout_width">match_parent</item>
+ </style>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 3ef8316..19a1b11 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -255,15 +255,10 @@ public class AlternateRecentsComponent {
/** Loads the first task thumbnail */
Bitmap loadFirstTaskThumbnail() {
SystemServicesProxy ssp = mSystemServicesProxy;
- List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(1,
- UserHandle.CURRENT.getIdentifier());
- for (ActivityManager.RecentTaskInfo t : tasks) {
- // Skip tasks in the home stack
- if (ssp.isInHomeStack(t.persistentId)) {
- return null;
- }
+ List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
- return ssp.getTaskThumbnail(t.persistentId);
+ for (ActivityManager.RunningTaskInfo t : tasks) {
+ return ssp.getTaskThumbnail(t.id);
}
return null;
}
@@ -286,17 +281,6 @@ public class AlternateRecentsComponent {
return (tasks.size() > 1);
}
- /** Returns whether the base intent of the top task stack was launched with the flag
- * Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS. */
- boolean isTopTaskExcludeFromRecents(List<ActivityManager.RecentTaskInfo> tasks) {
- if (tasks.size() > 0) {
- ActivityManager.RecentTaskInfo t = tasks.get(0);
- Console.log(t.baseIntent.toString());
- return (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
- }
- return false;
- }
-
/** Converts from the device rotation to the degree */
float getDegreesForRotation(int value) {
switch (value) {
@@ -416,16 +400,14 @@ public class AlternateRecentsComponent {
}
// Otherwise, Recents is not the front-most activity and we should animate into it. If
- // the activity at the root of the top task stack is excluded from recents, or if that
- // task stack is in the home stack, then we just do a simple transition. Otherwise, we
- // animate to the rects defined by the Recents service, which can differ depending on the
- // number of items in the list.
+ // the activity at the root of the top task stack in the home stack, then we just do a
+ // simple transition. Otherwise, we animate to the rects defined by the Recents service,
+ // which can differ depending on the number of items in the list.
List<ActivityManager.RecentTaskInfo> recentTasks =
- ssp.getRecentTasks(4, UserHandle.CURRENT.getIdentifier());
+ ssp.getRecentTasks(2, UserHandle.CURRENT.getIdentifier());
Rect taskRect = hasMultipleRecentsTask(recentTasks) ? mMultipleCountFirstTaskRect :
mSingleCountFirstTaskRect;
- boolean isTaskExcludedFromRecents = isTopTaskExcludeFromRecents(recentTasks);
- boolean useThumbnailTransition = !isTopTaskHome && !isTaskExcludedFromRecents &&
+ boolean useThumbnailTransition = !isTopTaskHome &&
hasValidTaskRects();
if (useThumbnailTransition) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java b/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java
deleted file mode 100644
index 95ab8e8..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.android.systemui.recents;
-
-import android.animation.TimeInterpolator;
-
-/**
- * A pre-baked bezier-curved interpolator for quantum-paper transitions.
- */
-public class BakedBezierInterpolator implements TimeInterpolator {
- public static final BakedBezierInterpolator INSTANCE = new BakedBezierInterpolator();
-
- /**
- * Use the INSTANCE variable instead of instantiating.
- */
- private BakedBezierInterpolator() {
- super();
- }
-
- /**
- * Lookup table values.
- * Generated using a Bezier curve from (0,0) to (1,1) with control points:
- * P0 (0,0)
- * P1 (0.4, 0)
- * P2 (0.2, 1.0)
- * P3 (1.0, 1.0)
- *
- * Values sampled with x at regular intervals between 0 and 1.
- */
- private static final float[] VALUES = new float[] {
- 0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f,
- 0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f,
- 0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f,
- 0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f,
- 0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f,
- 0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f,
- 0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f,
- 0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f,
- 0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f,
- 0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f
- };
-
- private static final float STEP_SIZE = 1.0f / (VALUES.length - 1);
-
- @Override
- public float getInterpolation(float input) {
- if (input >= 1.0f) {
- return 1.0f;
- }
-
- if (input <= 0f) {
- return 0f;
- }
-
- int position = Math.min(
- (int)(input * (VALUES.length - 1)),
- VALUES.length - 2);
-
- float quantized = position * STEP_SIZE;
- float difference = input - quantized;
- float weight = difference / STEP_SIZE;
-
- return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]);
- }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 1d6a76c..90998da 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -32,7 +32,7 @@ public class Constants {
// Enables the use of theme colors as the task bar background
public static final boolean EnableTaskBarThemeColors = true;
// Enables the info pane on long-press
- public static final boolean EnableInfoPane = true;
+ public static final boolean EnableInfoPane = false;
// Enables the search bar layout
public static final boolean EnableSearchLayout = true;
// Enables the dynamic shadows behind each task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 463cf74..9afc1cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -23,6 +23,8 @@ import android.content.res.Resources;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.TypedValue;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import com.android.systemui.R;
@@ -42,6 +44,8 @@ public class RecentsConfiguration {
public float animationPxMovementPerSecond;
+ public Interpolator defaultBezierInterpolator;
+
public int filteringCurrentViewsMinAnimDuration;
public int filteringNewViewsMinAnimDuration;
public int taskBarEnterAnimDuration;
@@ -121,7 +125,6 @@ public class RecentsConfiguration {
res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
-
taskBarViewDefaultBackgroundColor =
res.getColor(R.color.recents_task_bar_default_background_color);
taskBarViewDefaultTextColor =
@@ -131,6 +134,9 @@ public class RecentsConfiguration {
taskBarViewDarkTextColor =
res.getColor(R.color.recents_task_bar_dark_text_color);
+ defaultBezierInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
+
// Update the search widget id
SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index f3e411f..1ca0476 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -35,7 +35,6 @@ import com.android.systemui.recents.model.TaskStack;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -362,7 +361,7 @@ public class RecentsTaskLoader {
return mSystemServicesProxy;
}
- private List<ActivityManager.RecentTaskInfo> getRecentTasks(Context context) {
+ private List<ActivityManager.RecentTaskInfo> getRecentTasks() {
long t1 = System.currentTimeMillis();
SystemServicesProxy ssp = mSystemServicesProxy;
@@ -375,23 +374,6 @@ public class RecentsTaskLoader {
Console.log(Constants.Log.App.TaskDataLoader,
"[RecentsTaskLoader|tasks]", "" + tasks.size());
- // Remove home/recents tasks
- Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
- while (iter.hasNext()) {
- ActivityManager.RecentTaskInfo t = iter.next();
-
- // Skip tasks in the home stack
- if (ssp.isInHomeStack(t.persistentId)) {
- iter.remove();
- continue;
- }
- // Skip tasks from this Recents package
- if (t.baseIntent.getComponent().getPackageName().equals(context.getPackageName())) {
- iter.remove();
- continue;
- }
- }
-
return tasks;
}
@@ -408,7 +390,7 @@ public class RecentsTaskLoader {
// Get the recent tasks
SystemServicesProxy ssp = mSystemServicesProxy;
- List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(context);
+ List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks();
// Add each task to the task stack
t1 = System.currentTimeMillis();
@@ -554,14 +536,16 @@ public class RecentsTaskLoader {
}
/** Completely removes the resource data from the pool. */
- public void deleteTaskData(Task t) {
+ public void deleteTaskData(Task t, boolean notifyTaskDataUnloaded) {
Console.log(Constants.Log.App.TaskDataLoader,
"[RecentsTaskLoader|deleteTask]", t);
mLoadQueue.removeTask(t);
mThumbnailCache.remove(t.key);
mApplicationIconCache.remove(t.key);
- t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon);
+ if (notifyTaskDataUnloaded) {
+ t.notifyTaskDataUnloaded(mDefaultThumbnail, mDefaultApplicationIcon);
+ }
}
/** Stops the task loader and clears all pending tasks */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index b41555f..8d82883 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -30,7 +30,6 @@ import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
-import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -40,6 +39,7 @@ import android.os.UserManager;
import android.util.Pair;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
import java.util.Random;
@@ -54,7 +54,7 @@ public class SystemServicesProxy {
IPackageManager mIpm;
UserManager mUm;
SearchManager mSm;
- String mPackage;
+ String mRecentsPackage;
ComponentName mAssistComponent;
Bitmap mDummyIcon;
@@ -67,7 +67,7 @@ public class SystemServicesProxy {
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
mIpm = AppGlobals.getPackageManager();
mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
- mPackage = context.getPackageName();
+ mRecentsPackage = context.getPackageName();
// Resolve the assist intent
Intent assist = mSm.getAssistIntent(context, false);
@@ -83,14 +83,14 @@ public class SystemServicesProxy {
}
/** Returns a list of the recents tasks */
- public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
+ public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId) {
if (mAm == null) return null;
// If we are mocking, then create some recent tasks
if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
ArrayList<ActivityManager.RecentTaskInfo> tasks =
new ArrayList<ActivityManager.RecentTaskInfo>();
- int count = Math.min(numTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount);
+ int count = Math.min(numLatestTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount);
for (int i = 0; i < count; i++) {
// Create a dummy component name
int packageIndex = i % Constants.DebugFlags.App.SystemServicesProxyMockPackageCount;
@@ -114,9 +114,43 @@ public class SystemServicesProxy {
return tasks;
}
- return mAm.getRecentTasksForUser(numTasks,
+ // Remove home/recents/excluded tasks
+ int minNumTasksToQuery = 10;
+ int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
+ List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery,
ActivityManager.RECENT_IGNORE_UNAVAILABLE |
- ActivityManager.RECENT_INCLUDE_PROFILES, userId);
+ ActivityManager.RECENT_INCLUDE_PROFILES |
+ ActivityManager.RECENT_WITH_EXCLUDED, userId);
+ boolean isFirstValidTask = true;
+ Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
+ while (iter.hasNext()) {
+ ActivityManager.RecentTaskInfo t = iter.next();
+
+ // NOTE: The order of these checks happens in the expected order of the traversal of the
+ // tasks
+
+ // Skip tasks from this Recents package
+ if (t.baseIntent.getComponent().getPackageName().equals(mRecentsPackage)) {
+ iter.remove();
+ continue;
+ }
+ // Check the first non-recents task, include this task even if it is marked as excluded
+ // from recents. In other words, only remove excluded tasks if it is not the first task
+ boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+ == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+ if (isExcluded && !isFirstValidTask) {
+ iter.remove();
+ continue;
+ }
+ isFirstValidTask = false;
+ // Skip tasks in the home stack
+ if (isInHomeStack(t.persistentId)) {
+ iter.remove();
+ continue;
+ }
+ }
+
+ return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
}
/** Returns a list of the running tasks */
@@ -165,11 +199,12 @@ public class SystemServicesProxy {
}
/** Removes the task and kills the process */
- public void removeTask(int taskId) {
+ public void removeTask(int taskId, boolean isDocument) {
if (mAm == null) return;
if (Constants.DebugFlags.App.EnableSystemServicesProxy) return;
- mAm.removeTask(taskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);
+ // Remove the task, and only kill the process if it is not a document
+ mAm.removeTask(taskId, isDocument ? 0 : ActivityManager.REMOVE_TASK_KILL_PROCESS);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
index b602f84..46e6ee9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
@@ -18,6 +18,7 @@ package com.android.systemui.recents;
import android.graphics.Color;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
/* Common code */
public class Utilities {
@@ -54,12 +55,15 @@ public class Utilities {
0.0722f * Color.blue(color));
}
- /** Returns the ideal text color to draw on top of a specified background color. */
- public static int getIdealTextColorForBackgroundColor(int color) {
- RecentsConfiguration configuration = RecentsConfiguration.getInstance();
+ /** Returns the ideal color to draw on top of a specified background color. */
+ public static int getIdealColorForBackgroundColor(int color, int lightRes, int darkRes) {
int greyscale = colorToGreyscale(color);
- return (greyscale < 128) ? configuration.taskBarViewLightTextColor :
- configuration.taskBarViewDarkTextColor;
-
+ return (greyscale < 128) ? lightRes : darkRes;
+ }
+ /** Returns the ideal drawable to draw on top of a specified background color. */
+ public static Drawable getIdealResourceForBackgroundColor(int color, Drawable lightRes,
+ Drawable darkRes) {
+ int greyscale = colorToGreyscale(color);
+ return (greyscale < 128) ? lightRes : darkRes;
}
}
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 8168619..a6d7e67 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -346,7 +346,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
RecentsTaskLoader.getInstance().getSystemServicesProxy()
.moveTaskToFront(task.key.id, opts);
} else {
- // Launch the activity with the desired animation
+ // Launch the activity anew with the desired animation
Intent i = new Intent(task.key.baseIntent);
i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
| Intent.FLAG_ACTIVITY_TASK_ON_HOME
@@ -361,6 +361,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
} catch (ActivityNotFoundException anfe) {
Console.logError(getContext(), "Could not start Activity");
}
+
+ // And clean up the old task
+ onTaskRemoved(task);
}
Console.logTraceTime(Constants.Log.App.TimeRecentsLaunchTask,
@@ -390,6 +393,22 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
.addNextIntentWithParentStack(intent).startActivities();
}
+ @Override
+ public void onTaskRemoved(Task t) {
+ // Remove any stored data from the loader. We currently don't bother notifying the views
+ // that the data has been unloaded because at the point we call onTaskRemoved(), the views
+ // either don't need to be updated, or have already been removed.
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ loader.deleteTaskData(t, false);
+
+ // Remove the old task from activity manager
+ int flags = t.key.baseIntent.getFlags();
+ boolean isDocument = (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) ==
+ Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+ RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(t.key.id,
+ isDocument);
+ }
+
/**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
@Override
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 c6cb812..07caa1b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -16,7 +16,10 @@
package com.android.systemui.recents.views;
+import android.animation.ValueAnimator;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -32,9 +35,13 @@ import com.android.systemui.recents.model.Task;
class TaskBarView extends FrameLayout {
Task mTask;
+ ImageView mDismissButton;
ImageView mApplicationIcon;
TextView mActivityDescription;
+ Drawable mLightDismissDrawable;
+ Drawable mDarkDismissDrawable;
+
public TaskBarView(Context context) {
this(context, null);
}
@@ -49,6 +56,9 @@ class TaskBarView extends FrameLayout {
public TaskBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ Resources res = context.getResources();
+ mLightDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_light);
+ mDarkDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_dark);
}
@Override
@@ -56,6 +66,28 @@ class TaskBarView extends FrameLayout {
// Initialize the icon and description views
mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
mActivityDescription = (TextView) findViewById(R.id.activity_description);
+ mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
+ }
+
+ /** Synchronizes this bar view's properties with the task's transform */
+ void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
+ TaskViewTransform toTransform, int duration) {
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
+ if (duration > 0) {
+ if (animateFromTransform != null) {
+ mDismissButton.setAlpha(animateFromTransform.dismissAlpha);
+ }
+ mDismissButton.animate()
+ .alpha(toTransform.dismissAlpha)
+ .setStartDelay(0)
+ .setDuration(duration)
+ .setInterpolator(config.defaultBezierInterpolator)
+ .withLayer()
+ .start();
+ } else {
+ mDismissButton.setAlpha(toTransform.dismissAlpha);
+ }
+ mDismissButton.invalidate();
}
/** Binds the bar view to the task */
@@ -74,7 +106,10 @@ class TaskBarView extends FrameLayout {
int tint = t.colorPrimary;
if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) {
setBackgroundColor(tint);
- mActivityDescription.setTextColor(Utilities.getIdealTextColorForBackgroundColor(tint));
+ mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint,
+ configuration.taskBarViewLightTextColor, configuration.taskBarViewDarkTextColor));
+ mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint,
+ mLightDismissDrawable, mDarkDismissDrawable));
} else {
setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor);
mActivityDescription.setTextColor(configuration.taskBarViewDefaultTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
index c6c29a6..f1c362a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
@@ -30,7 +30,6 @@ import android.util.AttributeSet;
import android.widget.Button;
import android.widget.FrameLayout;
import com.android.systemui.R;
-import com.android.systemui.recents.BakedBezierInterpolator;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.Utilities;
@@ -111,7 +110,8 @@ class TaskInfoView extends FrameLayout {
int duration = Utilities.calculateTranslationAnimationDuration((int) mMaxClipRadius);
mCircularClipAnimator = ObjectAnimator.ofFloat(this, "circularClipRadius", toRadius);
mCircularClipAnimator.setDuration(duration);
- mCircularClipAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE);
+ mCircularClipAnimator.setInterpolator(
+ RecentsConfiguration.getInstance().defaultBezierInterpolator);
mCircularClipAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -143,7 +143,7 @@ class TaskInfoView extends FrameLayout {
.scaleX(1f)
.scaleY(1f)
.setDuration(duration)
- .setInterpolator(BakedBezierInterpolator.INSTANCE)
+ .setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator)
.withLayer()
.start();
}
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 ad0f2f82..b64225e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -23,6 +23,7 @@ import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Region;
@@ -36,7 +37,6 @@ import android.view.ViewParent;
import android.widget.FrameLayout;
import android.widget.OverScroller;
import com.android.systemui.R;
-import com.android.systemui.recents.BakedBezierInterpolator;
import com.android.systemui.recents.Console;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
@@ -60,6 +60,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
interface TaskStackViewCallbacks {
public void onTaskLaunched(TaskStackView stackView, TaskView tv, TaskStack stack, Task t);
public void onTaskAppInfoLaunched(Task t);
+ public void onTaskRemoved(Task t);
}
TaskStack mStack;
@@ -168,6 +169,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
transform.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
}
+ // Set the alphas
+ transform.dismissAlpha = Math.max(-1f, Math.min(0f, t)) + 1f;
+
// Update the rect and visibility
transform.rect.set(mTaskRect);
if (t < -(numPeekCards + 1)) {
@@ -336,7 +340,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll);
mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
curScroll, 250));
- mScrollAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE);
+ mScrollAnimator.setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator);
mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
@@ -1034,6 +1038,15 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
+ @Override
+ public void onTaskDismissed(TaskView tv) {
+ Task task = tv.getTask();
+ // Remove the task from the view
+ mStack.removeTask(task);
+ // Notify the callback that we've removed the task and it can clean up after it
+ mCb.onTaskRemoved(task);
+ }
+
/**** View.OnClickListener Implementation ****/
@Override
@@ -1093,6 +1106,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
@Override
public void onComponentRemoved(Set<ComponentName> cns) {
+ RecentsConfiguration config = RecentsConfiguration.getInstance();
// For other tasks, just remove them directly if they no longer exist
ArrayList<Task> tasks = mStack.getTasks();
for (int i = tasks.size() - 1; i >= 0; i--) {
@@ -1475,17 +1489,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
@Override
public void onChildDismissed(View v) {
TaskView tv = (TaskView) v;
- Task task = tv.getTask();
-
- // Remove the task from the view
- mSv.mStack.removeTask(task);
-
- // Remove any stored data from the loader
- RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
- loader.deleteTaskData(task);
-
- // Remove the task from activity manager
- RecentsTaskLoader.getInstance().getSystemServicesProxy().removeTask(tv.getTask().key.id);
+ mSv.onTaskDismissed(tv);
// Disable HW layers
mSv.decHwLayersRefCount("swipeComplete");
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 b03f389..5fad629 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -31,7 +31,6 @@ import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
import com.android.systemui.R;
-import com.android.systemui.recents.BakedBezierInterpolator;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.model.Task;
@@ -46,6 +45,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener,
public void onTaskInfoPanelShown(TaskView tv);
public void onTaskInfoPanelHidden(TaskView tv);
public void onTaskAppInfoClicked(TaskView tv);
+ public void onTaskDismissed(TaskView tv);
// public void onTaskViewReboundToTask(TaskView tv, Task t);
}
@@ -143,6 +143,10 @@ public class TaskView extends FrameLayout implements View.OnClickListener,
int minZ = config.taskViewTranslationZMinPx;
int incZ = config.taskViewTranslationZIncrementPx;
+ // Update the bar view
+ mBarView.updateViewPropertiesToTaskTransform(animateFromTransform, toTransform, duration);
+
+ // Update this task view
if (duration > 0) {
if (animateFromTransform != null) {
setTranslationY(animateFromTransform.translationY);
@@ -161,7 +165,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener,
.scaleY(toTransform.scale)
.alpha(toTransform.alpha)
.setDuration(duration)
- .setInterpolator(BakedBezierInterpolator.INSTANCE)
+ .setInterpolator(config.defaultBezierInterpolator)
.withLayer()
.setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
@@ -221,8 +225,8 @@ public class TaskView extends FrameLayout implements View.OnClickListener,
mBarView.setAlpha(0f);
mBarView.animate()
.alpha(1f)
- .setStartDelay(235)
- .setInterpolator(BakedBezierInterpolator.INSTANCE)
+ .setStartDelay(250)
+ .setInterpolator(config.defaultBezierInterpolator)
.setDuration(config.taskBarEnterAnimDuration)
.withLayer()
.start();
@@ -234,7 +238,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener,
mBarView.animate()
.alpha(0f)
.setStartDelay(0)
- .setInterpolator(BakedBezierInterpolator.INSTANCE)
+ .setInterpolator(config.defaultBezierInterpolator)
.setDuration(config.taskBarExitAnimDuration)
.withLayer()
.withEndAction(new Runnable() {
@@ -252,7 +256,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener,
animate().translationX(config.taskViewRemoveAnimTranslationXPx)
.alpha(0f)
.setStartDelay(0)
- .setInterpolator(BakedBezierInterpolator.INSTANCE)
+ .setInterpolator(config.defaultBezierInterpolator)
.setDuration(config.taskViewRemoveAnimDuration)
.withLayer()
.withEndAction(new Runnable() {
@@ -310,7 +314,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener,
mInfoView.animate()
.alpha(0f)
.setDuration(config.taskViewInfoPaneAnimDuration)
- .setInterpolator(BakedBezierInterpolator.INSTANCE)
+ .setInterpolator(config.defaultBezierInterpolator)
.withLayer()
.withEndAction(new Runnable() {
@Override
@@ -380,6 +384,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener,
mInfoView.rebindToTask(mTask, reloadingTaskData);
// Rebind any listeners
mBarView.mApplicationIcon.setOnClickListener(this);
+ mBarView.mDismissButton.setOnClickListener(this);
mInfoView.mAppInfoButton.setOnClickListener(this);
}
mTaskDataLoaded = true;
@@ -405,6 +410,15 @@ public class TaskView extends FrameLayout implements View.OnClickListener,
hideInfoPane();
} else if (v == mBarView.mApplicationIcon) {
mCb.onTaskIconClicked(this);
+ } else if (v == mBarView.mDismissButton) {
+ // Animate out the view and call the callback
+ final TaskView tv = this;
+ animateRemoval(new Runnable() {
+ @Override
+ public void run() {
+ mCb.onTaskDismissed(tv);
+ }
+ });
} else if (v == mInfoView.mAppInfoButton) {
mCb.onTaskAppInfoClicked(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 0748bbb..e6391a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -24,6 +24,7 @@ public class TaskViewTransform {
public int translationY = 0;
public float scale = 1f;
public float alpha = 1f;
+ public float dismissAlpha = 1f;
public boolean visible = false;
public Rect rect = new Rect();
float t;
@@ -36,6 +37,7 @@ public class TaskViewTransform {
translationY = o.translationY;
scale = o.scale;
alpha = o.alpha;
+ dismissAlpha = o.dismissAlpha;
visible = o.visible;
rect.set(o.rect);
t = o.t;
@@ -44,6 +46,6 @@ public class TaskViewTransform {
@Override
public String toString() {
return "TaskViewTransform y: " + translationY + " scale: " + scale + " alpha: " + alpha +
- " visible: " + visible + " rect: " + rect;
+ " visible: " + visible + " rect: " + rect + " dismissAlpha: " + dismissAlpha;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 327e715..1747e6e 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -119,7 +119,6 @@ public class BrightnessController implements ToggleSlider.Listener {
}
};
mBrightnessObserver = new BrightnessObserver(mHandler);
- mBrightnessObserver.startObserving();
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mMinimumBacklight = pm.getMinimumScreenBrightnessSetting();
@@ -128,13 +127,6 @@ public class BrightnessController implements ToggleSlider.Listener {
mAutomaticAvailable = context.getResources().getBoolean(
com.android.internal.R.bool.config_automatic_brightness_available);
mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
-
- // Update the slider and mode before attaching the listener so we don't receive the
- // onChanged notifications for the initial values.
- updateMode();
- updateSlider();
-
- control.setOnChangedListener(this);
}
public void addStateChangedCallback(BrightnessStateChangeCallback cb) {
@@ -150,11 +142,24 @@ public class BrightnessController implements ToggleSlider.Listener {
// Do nothing
}
+ public void registerCallbacks() {
+ mBrightnessObserver.startObserving();
+ mUserTracker.startTracking();
+
+ // Update the slider and mode before attaching the listener so we don't receive the
+ // onChanged notifications for the initial values.
+ updateMode();
+ updateSlider();
+
+ mControl.setOnChangedListener(this);
+ }
+
/** Unregister all call backs, both to and from the controller */
public void unregisterCallbacks() {
mBrightnessObserver.stopObserving();
mChangeCallbacks.clear();
mUserTracker.stopTracking();
+ mControl.setOnChangedListener(null);
}
public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index bd5e5e8..27881c4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -92,6 +92,7 @@ public class BrightnessDialog extends Dialog implements
mBrightnessController = new BrightnessController(getContext(),
(ImageView) findViewById(R.id.brightness_icon),
(ToggleSlider) findViewById(R.id.brightness_slider));
+ mBrightnessController.registerCallbacks();
dismissBrightnessDialog(mBrightnessDialogLongTimeout);
mBrightnessController.addStateChangedCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
index 036bd4f..f8ff616 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
@@ -29,9 +29,6 @@ public abstract class CurrentUserTracker extends BroadcastReceiver {
private int mCurrentUserId;
public CurrentUserTracker(Context context) {
- IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- context.registerReceiver(this, filter);
- mCurrentUserId = ActivityManager.getCurrentUser();
mContext = context;
}
@@ -50,6 +47,12 @@ public abstract class CurrentUserTracker extends BroadcastReceiver {
}
}
+ public void startTracking() {
+ mCurrentUserId = ActivityManager.getCurrentUser();
+ IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+ mContext.registerReceiver(this, filter);
+ }
+
public void stopTracking() {
mContext.unregisterReceiver(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
index 7d38058..4b78072 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
@@ -62,7 +62,6 @@ public class ToggleSlider extends RelativeLayout {
mToggle = (CompoundButton) findViewById(R.id.toggle);
mToggle.setOnCheckedChangeListener(mCheckListener);
- mToggle.setBackground(res.getDrawable(R.drawable.status_bar_toggle_button));
mSlider = (SeekBar) findViewById(R.id.slider);
mSlider.setOnSeekBarChangeListener(mSeekListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index ecefc39..898f06e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1029,6 +1029,9 @@ public abstract class BaseStatusBar extends SystemUI implements
}
protected void addNotificationViews(NotificationData.Entry entry) {
+ if (entry == null) {
+ return;
+ }
// Add the expanded view and icon.
int pos = mNotificationData.add(entry);
if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 281bd2d..4bd0e1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -112,7 +112,7 @@ public abstract class ExpandableView extends FrameLayout {
* @return The desired notification height.
*/
public int getIntrinsicHeight() {
- return mActualHeight;
+ return getHeight();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index 864c597..451c5c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -34,21 +34,6 @@ public class NotificationOverflowContainer extends ActivatableNotificationView {
}
@Override
- public void setActualHeight(int currentHeight, boolean notifyListeners) {
- // noop
- }
-
- @Override
- public int getActualHeight() {
- return getHeight();
- }
-
- @Override
- public void setClipTopAmount(int clipTopAmount) {
- // noop
- }
-
- @Override
protected void onFinishInflate() {
super.onFinishInflate();
mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
new file mode 100644
index 0000000..c26f15e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.ContactsContract;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+
+import com.android.systemui.R;
+import com.android.systemui.settings.UserSwitcherHostView;
+import com.android.systemui.statusbar.policy.UserInfoController;
+
+/**
+ * Image button for the multi user switcher.
+ */
+public class MultiUserSwitch extends ImageButton implements View.OnClickListener,
+ UserInfoController.OnUserInfoChangedListener {
+
+ private ViewGroup mOverlayParent;
+
+ public MultiUserSwitch(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ setOnClickListener(this);
+ }
+
+ public void setOverlayParent(ViewGroup parent) {
+ mOverlayParent = parent;
+ }
+
+ @Override
+ public void onClick(View v) {
+ final UserManager um = UserManager.get(getContext());
+ if (um.isUserSwitcherEnabled()) {
+ final UserSwitcherHostView switcher =
+ (UserSwitcherHostView) LayoutInflater.from(getContext()).inflate(
+ R.layout.user_switcher_host, mOverlayParent, false);
+ switcher.setFinishRunnable(new Runnable() {
+ @Override
+ public void run() {
+ mOverlayParent.removeView(switcher);
+ }
+ });
+ switcher.refreshUsers();
+ mOverlayParent.addView(switcher);
+ } else {
+ Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
+ getContext(), v, ContactsContract.Profile.CONTENT_URI,
+ ContactsContract.QuickContact.MODE_LARGE, null);
+ getContext().startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+ }
+ }
+
+ public void setUserInfoController(UserInfoController userInfoController) {
+ userInfoController.addListener(this);
+ }
+
+ @Override
+ public void onUserInfoChanged(String name, Drawable picture) {
+ setImageDrawable(picture);
+ }
+}
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 f63ba9c..6132ed2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -24,9 +24,11 @@ import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import android.widget.LinearLayout;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableView;
@@ -56,10 +58,18 @@ public class NotificationPanelView extends PanelView implements
private int mTrackingPointer;
private VelocityTracker mVelocityTracker;
private boolean mTracking;
+
+ /**
+ * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
+ * intercepted yet.
+ */
+ private boolean mIntercepting;
private boolean mQsExpanded;
private float mInitialHeightOnTouch;
private float mInitialTouchX;
private float mInitialTouchY;
+ private float mLastTouchX;
+ private float mLastTouchY;
private float mQsExpansionHeight;
private int mQsMinExpansionHeight;
private int mQsMaxExpansionHeight;
@@ -93,6 +103,7 @@ public class NotificationPanelView extends PanelView implements
super.onFinishInflate();
mHeader = (StatusBarHeaderView) findViewById(R.id.header);
mHeader.getBackgroundView().setOnClickListener(this);
+ mHeader.setOverlayParent(this);
mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
mStackScrollerContainer = findViewById(R.id.notification_container_parent);
mQsContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container);
@@ -144,7 +155,6 @@ public class NotificationPanelView extends PanelView implements
public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
mQsExpansionEnabled = qsExpansionEnabled;
- mHeader.setExpansionEnabled(qsExpansionEnabled);
}
public void closeQs() {
@@ -193,6 +203,7 @@ public class NotificationPanelView extends PanelView implements
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
+ mIntercepting = true;
mInitialTouchY = y;
mInitialTouchX = x;
initVelocityTracker();
@@ -215,6 +226,16 @@ public class NotificationPanelView extends PanelView implements
case MotionEvent.ACTION_MOVE:
final float h = y - mInitialTouchY;
trackMovement(event);
+ if (mTracking) {
+
+ // Already tracking because onOverscrolled was called. We need to update here
+ // so we don't stop for a frame until the next touch event gets handled in
+ // onTouchEvent.
+ setQsExpansion(h + mInitialHeightOnTouch);
+ trackMovement(event);
+ mIntercepting = false;
+ return true;
+ }
if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldIntercept(mInitialTouchX, mInitialTouchY, h)) {
onQsExpansionStarted();
@@ -222,14 +243,29 @@ public class NotificationPanelView extends PanelView implements
mInitialTouchY = y;
mInitialTouchX = x;
mTracking = true;
+ mIntercepting = false;
return true;
}
break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ mIntercepting = false;
+ break;
}
return !mQsExpanded && super.onInterceptTouchEvent(event);
}
@Override
+ public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+
+ // Block request so we can still intercept the scrolling when QS is expanded.
+ if (!mQsExpanded) {
+ super.requestDisallowInterceptTouchEvent(disallowIntercept);
+ }
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
// TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
// implementation.
@@ -297,11 +333,26 @@ public class NotificationPanelView extends PanelView implements
return mQsExpanded || super.onTouchEvent(event);
}
+ @Override
+ public void onOverscrolled(int amount) {
+ if (mIntercepting) {
+ onQsExpansionStarted(amount);
+ mInitialHeightOnTouch = mQsExpansionHeight;
+ mInitialTouchY = mLastTouchY;
+ mInitialTouchX = mLastTouchX;
+ mTracking = true;
+ }
+ }
+
private void onQsExpansionStarted() {
+ onQsExpansionStarted(0);
+ }
+
+ private void onQsExpansionStarted(int overscrollAmount) {
cancelAnimation();
// Reset scroll position and apply that position to the expanded height.
- float height = mQsExpansionHeight - mScrollView.getScrollY();
+ float height = mQsExpansionHeight - mScrollView.getScrollY() - overscrollAmount;
mScrollView.scrollTo(0, 0);
setQsExpansion(height);
}
@@ -359,6 +410,8 @@ public class NotificationPanelView extends PanelView implements
private void trackMovement(MotionEvent event) {
if (mVelocityTracker != null) mVelocityTracker.addMovement(event);
+ mLastTouchX = event.getX();
+ mLastTouchY = event.getY();
}
private void initVelocityTracker() {
@@ -412,11 +465,8 @@ public class NotificationPanelView extends PanelView implements
if (!mQsExpansionEnabled) {
return false;
}
- View headerView = mStatusBar.getBarState() == StatusBarState.KEYGUARD && !mQsExpanded
- ? mKeyguardStatusView
- : mHeader;
- boolean onHeader = x >= headerView.getLeft() && x <= headerView.getRight()
- && y >= headerView.getTop() && y <= headerView.getBottom();
+ boolean onHeader = x >= mHeader.getLeft() && x <= mHeader.getRight()
+ && y >= mHeader.getTop() && y <= mHeader.getBottom();
if (mQsExpanded) {
return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0);
} else {
@@ -425,6 +475,35 @@ public class NotificationPanelView extends PanelView implements
}
@Override
+ public void setVisibility(int visibility) {
+ int oldVisibility = getVisibility();
+ super.setVisibility(visibility);
+ if (visibility != oldVisibility) {
+ reparentStatusIcons(visibility == VISIBLE);
+ }
+ }
+
+ /**
+ * When the notification panel gets expanded, we need to move the status icons in the header
+ * card.
+ */
+ private void reparentStatusIcons(boolean toHeader) {
+ if (mStatusBar == null) {
+ return;
+ }
+ LinearLayout systemIcons = mStatusBar.getSystemIcons();
+ if (systemIcons.getParent() != null) {
+ ((ViewGroup) systemIcons.getParent()).removeView(systemIcons);
+ }
+ if (toHeader) {
+ mHeader.attachSystemIcons(systemIcons);
+ } else {
+ mHeader.onSystemIconsDetached();
+ mStatusBar.reattachSystemIcons();
+ }
+ }
+
+ @Override
protected boolean isScrolledToBottom() {
if (!isInSettings()) {
return mNotificationStackScroller.isScrolledToBottom();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
index 46484f3..ba0b66e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
@@ -27,6 +27,7 @@ import android.widget.ScrollView;
public class ObservableScrollView extends ScrollView {
private Listener mListener;
+ private int mLastOverscrollAmount;
public ObservableScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -58,7 +59,25 @@ public class ObservableScrollView extends ScrollView {
}
}
+ @Override
+ protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,
+ int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY,
+ boolean isTouchEvent) {
+ mLastOverscrollAmount = Math.max(0, scrollY + deltaY - getMaxScrollY());
+ return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY,
+ maxOverScrollX, maxOverScrollY, isTouchEvent);
+ }
+
+ @Override
+ protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
+ super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
+ if (mListener != null && mLastOverscrollAmount > 0) {
+ mListener.onOverscrolled(mLastOverscrollAmount);
+ }
+ }
+
public interface Listener {
void onScrollChanged();
+ void onOverscrolled(int amount);
}
}
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 92eee4e..4d09d6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -29,7 +29,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCE
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
@@ -112,6 +111,7 @@ import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
@@ -185,6 +185,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
LocationController mLocationController;
NetworkController mNetworkController;
RotationLockController mRotationLockController;
+ UserInfoController mUserInfoController;
int mNaturalBarHeight = -1;
int mIconSize = -1;
@@ -205,6 +206,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
// right-hand icons
LinearLayout mSystemIconArea;
+ LinearLayout mSystemIcons;
// left-hand icons
LinearLayout mStatusIcons;
@@ -221,7 +223,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
int mNotificationPanelGravity;
int mNotificationPanelMarginBottomPx;
float mNotificationPanelMinHeightFrac;
- boolean mNotificationPanelIsFullScreenWidth;
TextView mNotificationPanelDebugText;
// settings
@@ -230,7 +231,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
QuickSettingsContainerView mSettingsContainer;
// top bar
- View mNotificationPanelHeader;
+ StatusBarHeaderView mHeader;
View mKeyguardStatusView;
KeyguardBottomAreaView mKeyguardBottomArea;
boolean mLeaveOpenOnKeyguardHide;
@@ -240,8 +241,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
String mKeyguardHotwordPhrase = "";
int mKeyguardMaxNotificationCount;
View mDateTimeView;
- View mClearButton;
- ImageView mHeaderFlipper;
// carrier/wifi label
private TextView mCarrierLabel;
@@ -249,7 +248,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private int mCarrierLabelHeight;
private TextView mEmergencyCallLabel;
private int mStatusBarHeaderHeight;
- private View mKeyguardCarrierLabel;
private boolean mShowCarrierInPanel = false;
@@ -554,8 +552,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
R.id.notification_panel);
mNotificationPanel.setStatusBar(this);
- mNotificationPanelIsFullScreenWidth =
- (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
// make the header non-responsive to clicks
mNotificationPanel.findViewById(R.id.header).setOnTouchListener(
@@ -609,6 +605,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mPixelFormat = PixelFormat.OPAQUE;
mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
+ mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons);
mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
@@ -627,39 +624,30 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
(NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
mKeyguardIconOverflowContainer.setOnActivatedListener(this);
- mKeyguardCarrierLabel = mStatusBarWindow.findViewById(R.id.keyguard_carrier_text);
mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
mStackScroller.addView(mKeyguardIconOverflowContainer);
mExpandedContents = mStackScroller;
- mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header);
+ mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
+ mHeader.setActivityStarter(this);
mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
mKeyguardBottomArea =
(KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
mKeyguardBottomArea.setActivityStarter(this);
mKeyguardIndicationTextView = (KeyguardIndicationTextView) mStatusBarWindow.findViewById(
R.id.keyguard_indication_text);
- mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button);
- mClearButton.setOnClickListener(mClearButtonListener);
- mClearButton.setAlpha(0f);
- mClearButton.setVisibility(View.INVISIBLE);
- mClearButton.setEnabled(false);
mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
- mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime);
+ mDateTimeView = mHeader.findViewById(R.id.datetime);
if (mDateTimeView != null) {
mDateTimeView.setOnClickListener(mClockClickListener);
mDateTimeView.setEnabled(true);
}
- mHeaderFlipper = (ImageView) mStatusBarWindow.findViewById(R.id.header_flipper);
-
- if (!mNotificationPanelIsFullScreenWidth) {
- mNotificationPanel.setSystemUiVisibility(
- View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
- View.STATUS_BAR_DISABLE_CLOCK);
- }
+ mNotificationPanel.setSystemUiVisibility(
+ View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
+ View.STATUS_BAR_DISABLE_CLOCK);
mTicker = new MyTicker(context, mStatusBarView);
@@ -680,6 +668,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
|| QuickSettings.DEBUG_GONE_TILES) {
mRotationLockController = new RotationLockController(mContext);
}
+ mUserInfoController = new UserInfoController(mContext);
final SignalClusterView signalCluster =
(SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
@@ -734,11 +723,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mFlipSettingsView = mSettingsContainer;
if (mSettingsContainer != null) {
mQS = new QuickSettings(mContext, mSettingsContainer);
- if (!mNotificationPanelIsFullScreenWidth) {
- mSettingsContainer.setSystemUiVisibility(
- View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS
- | View.STATUS_BAR_DISABLE_SYSTEM_INFO);
- }
mQS.setService(this);
mQS.setBar(mStatusBarView);
mQS.setup(mNetworkController, mBluetoothController, mBatteryController,
@@ -747,6 +731,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mQS = null; // fly away, be free
}
+ // User info. Trigger first load.
+ mHeader.setUserInfoController(mUserInfoController);
+ mUserInfoController.reloadUserInfo();
+
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBroadcastReceiver.onReceive(mContext,
new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
@@ -1118,19 +1106,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
if (mNavigationBarView != null) {
mNavigationBarView.setLayoutDirection(layoutDirection);
}
-
- if (mClearButton != null && mClearButton instanceof ImageView) {
- // Force asset reloading
- ((ImageView)mClearButton).setImageDrawable(null);
- ((ImageView)mClearButton).setImageResource(R.drawable.ic_notify_clear);
- }
-
- if (mHeaderFlipper != null) {
- // Force asset reloading
- mHeaderFlipper.setImageDrawable(null);
- mHeaderFlipper.setImageResource(R.drawable.ic_notify_quicksettings);
- }
-
refreshAllStatusBarIcons();
}
@@ -1301,38 +1276,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
+ " any=" + any + " clearable=" + clearable);
}
- if (mFlipSettingsView != null
- && mFlipSettingsView.getVisibility() == View.VISIBLE
- && mStackScroller.getVisibility() != View.VISIBLE) {
- // the flip settings panel is unequivocally showing; we should not be shown
- mClearButton.setVisibility(View.INVISIBLE);
- } else if (mClearButton.isShown()) {
- if (clearable != (mClearButton.getAlpha() == 1.0f)) {
- ObjectAnimator clearAnimation = ObjectAnimator.ofFloat(
- mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250);
- clearAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mClearButton.getAlpha() <= 0.0f) {
- mClearButton.setVisibility(View.INVISIBLE);
- }
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- if (mClearButton.getAlpha() <= 0.0f) {
- mClearButton.setVisibility(View.VISIBLE);
- }
- }
- });
- clearAnimation.start();
- }
- } else {
- mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
- mClearButton.setVisibility(clearable ? View.VISIBLE : View.INVISIBLE);
- }
- mClearButton.setEnabled(clearable);
-
final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
final boolean showDot = (any&&!areLightsOn());
if (showDot != (nlo.getAlpha() == 1.0f)) {
@@ -1917,13 +1860,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private void checkBarModes() {
if (mDemoMode) return;
- int sbMode = mStatusBarMode;
- if (panelsEnabled() && (mInteractingWindows & StatusBarManager.WINDOW_STATUS_BAR) != 0
- && mState != StatusBarState.KEYGUARD) {
- // if panels are expandable, force the status bar opaque on any interaction
- sbMode = MODE_OPAQUE;
- }
- checkBarMode(sbMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
+ checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
if (mNavigationBarView != null) {
checkBarMode(mNavigationBarMode,
mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
@@ -2449,10 +2386,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
final Context context = mContext;
final Resources res = context.getResources();
- if (mClearButton instanceof TextView) {
- ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button));
- }
-
// Update the QuickSettings container
if (mQS != null) mQS.updateResources();
@@ -2774,20 +2707,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mKeyguardBottomArea.setVisibility(View.VISIBLE);
mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
- mKeyguardCarrierLabel.setVisibility(View.VISIBLE);
- mNotificationPanelHeader.setVisibility(View.GONE);
mNotificationPanel.closeQs();
- mSettingsContainer.setKeyguardShowing(true);
} else {
mKeyguardStatusView.setVisibility(View.GONE);
mKeyguardBottomArea.setVisibility(View.GONE);
mKeyguardIndicationTextView.setVisibility(View.GONE);
- mKeyguardCarrierLabel.setVisibility(View.GONE);
- mNotificationPanelHeader.setVisibility(View.VISIBLE);
-
- mSettingsContainer.setKeyguardShowing(false);
}
+ mSettingsContainer.setKeyguardShowing(mState == StatusBarState.KEYGUARD);
+ mHeader.setKeyguardShowing(mState == StatusBarState.KEYGUARD);
updateStackScrollerState();
updatePublicMode();
@@ -2943,4 +2871,15 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
public ViewGroup getQuickSettingsOverlayParent() {
return mNotificationPanel;
}
+
+ public LinearLayout getSystemIcons() {
+ return mSystemIcons;
+ }
+
+ /**
+ * Reattaches the system icons to its normal parent in collapsed status bar.
+ */
+ public void reattachSystemIcons() {
+ mSystemIconArea.addView(mSystemIcons, 0);
+ }
}
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 e6de057..084bfcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -63,7 +63,7 @@ public class PhoneStatusBarView extends PanelBar {
}
@Override
- public void onAttachedToWindow() {
+ public void onFinishInflate() {
mBarTransitions.init();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index e1ef83a..005b0d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -460,6 +460,7 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
rebindMediaRouterAsCurrentUser();
}
};
+ mUserTracker.startTracking();
mNextAlarmObserver = new NextAlarmObserver(mHandler);
mNextAlarmObserver.startObserving();
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 9d33930..5527473 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -17,25 +17,52 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
+import android.content.Intent;
+import android.graphics.Outline;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.android.systemui.R;
+import com.android.systemui.settings.BrightnessController;
+import com.android.systemui.settings.ToggleSlider;
+import com.android.systemui.statusbar.policy.UserInfoController;
/**
* The view to manage the header area in the expanded status bar.
*/
-public class StatusBarHeaderView extends RelativeLayout {
+public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener {
private boolean mExpanded;
+ private boolean mKeyguardShowing;
+
private View mBackground;
- private View mFlipper;
+ private ViewGroup mSystemIconsContainer;
+ private View mDateTime;
+ private View mKeyguardCarrierText;
+ private MultiUserSwitch mMultiUserSwitch;
+ private View mDate;
+ private View mStatusIcons;
+ private View mSignalCluster;
+ private View mSettingsButton;
+ private View mBrightnessContainer;
private int mCollapsedHeight;
private int mExpandedHeight;
+ private int mKeyguardHeight;
+
+ private int mKeyguardWidth = ViewGroup.LayoutParams.MATCH_PARENT;
+ private int mNormalWidth;
+
+ private ActivityStarter mActivityStarter;
+ private BrightnessController mBrightnessController;
+
+ private final Rect mClipBounds = new Rect();
+ private final Outline mOutline = new Outline();
public StatusBarHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -45,19 +72,35 @@ public class StatusBarHeaderView extends RelativeLayout {
protected void onFinishInflate() {
super.onFinishInflate();
mBackground = findViewById(R.id.background);
- mFlipper = findViewById(R.id.header_flipper);
+ mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container);
+ mDateTime = findViewById(R.id.datetime);
+ mKeyguardCarrierText = findViewById(R.id.keyguard_carrier_text);
+ mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
+ mDate = findViewById(R.id.date);
+ mSettingsButton = findViewById(R.id.settings_button);
+ mSettingsButton.setOnClickListener(this);
+ mBrightnessContainer = findViewById(R.id.brightness_container);
+ mBrightnessController = new BrightnessController(getContext(),
+ (ImageView) findViewById(R.id.brightness_icon),
+ (ToggleSlider) findViewById(R.id.brightness_slider));
loadDimens();
}
private void loadDimens() {
- mCollapsedHeight = getResources().getDimensionPixelSize(
- R.dimen.status_bar_header_height);
+ mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height);
mExpandedHeight = getResources().getDimensionPixelSize(
R.dimen.status_bar_header_height_expanded);
+ mKeyguardHeight = getResources().getDimensionPixelSize(
+ R.dimen.status_bar_header_height_keyguard);
+ mNormalWidth = getLayoutParams().width;
+ }
+
+ public void setActivityStarter(ActivityStarter activityStarter) {
+ mActivityStarter = activityStarter;
}
public int getCollapsedHeight() {
- return mCollapsedHeight;
+ return mKeyguardShowing ? mKeyguardHeight : mCollapsedHeight;
}
public int getExpandedHeight() {
@@ -65,16 +108,82 @@ public class StatusBarHeaderView extends RelativeLayout {
}
public void setExpanded(boolean expanded) {
- if (expanded != mExpanded) {
- ViewGroup.LayoutParams lp = getLayoutParams();
- lp.height = expanded ? mExpandedHeight : mCollapsedHeight;
+ boolean changed = expanded != mExpanded;
+ mExpanded = expanded;
+ if (changed) {
+ updateHeights();
+ updateVisibilities();
+ updateSystemIconsLayoutParams();
+ updateBrightnessControllerState();
+ }
+ }
+
+ private void updateHeights() {
+ boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
+ int height;
+ if (mExpanded) {
+ height = mExpandedHeight;
+ } else if (onKeyguardAndCollapsed) {
+ height = mKeyguardHeight;
+ } else {
+ height = mCollapsedHeight;
+ }
+ ViewGroup.LayoutParams lp = getLayoutParams();
+ if (lp.height != height) {
+ lp.height = height;
setLayoutParams(lp);
- mExpanded = expanded;
+ }
+ int systemIconsContainerHeight = onKeyguardAndCollapsed ? mKeyguardHeight : mCollapsedHeight;
+ lp = mSystemIconsContainer.getLayoutParams();
+ if (lp.height != systemIconsContainerHeight) {
+ lp.height = systemIconsContainerHeight;
+ mSystemIconsContainer.setLayoutParams(lp);
+ }
+ lp = mMultiUserSwitch.getLayoutParams();
+ if (lp.height != systemIconsContainerHeight) {
+ lp.height = systemIconsContainerHeight;
+ mMultiUserSwitch.setLayoutParams(lp);
}
}
- public void setExpansionEnabled(boolean enabled) {
- mFlipper.setVisibility(enabled ? View.VISIBLE : View.GONE);
+ private void updateWidth() {
+ int width = mKeyguardShowing ? mKeyguardWidth : mNormalWidth;
+ ViewGroup.LayoutParams lp = getLayoutParams();
+ if (width != lp.width) {
+ lp.width = width;
+ setLayoutParams(lp);
+ }
+ }
+
+ private void updateVisibilities() {
+ boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
+ mBackground.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
+ mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
+ mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE);
+ mDate.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+ mSettingsButton.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+ mBrightnessContainer.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+ if (mStatusIcons != null) {
+ mStatusIcons.setVisibility(!mExpanded ? View.VISIBLE : View.GONE);
+ }
+ if (mSignalCluster != null) {
+ mSignalCluster.setVisibility(!mExpanded ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ private void updateSystemIconsLayoutParams() {
+ RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsContainer.getLayoutParams();
+ lp.addRule(RelativeLayout.START_OF, mExpanded
+ ? mSettingsButton.getId()
+ : mMultiUserSwitch.getId());
+ }
+
+ private void updateBrightnessControllerState() {
+ if (mExpanded) {
+ mBrightnessController.registerCallbacks();
+ } else {
+ mBrightnessController.unregisterCallbacks();
+ }
}
public void setExpansion(float height) {
@@ -89,9 +198,65 @@ public class StatusBarHeaderView extends RelativeLayout {
} else {
mBackground.setTranslationY(0);
}
+ setClipping(height);
+ }
+
+ private void setClipping(float height) {
+ mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height);
+ setClipBounds(mClipBounds);
+ mOutline.setRect(mClipBounds);
+ setOutline(mOutline);
}
public View getBackgroundView() {
return mBackground;
}
+
+ public void attachSystemIcons(LinearLayout systemIcons) {
+ mSystemIconsContainer.addView(systemIcons);
+ mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
+ mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
+ }
+
+ public void onSystemIconsDetached() {
+ if (mStatusIcons != null) {
+ mStatusIcons.setVisibility(View.VISIBLE);
+ }
+ if (mSignalCluster != null) {
+ mSignalCluster.setVisibility(View.VISIBLE);
+ }
+ mStatusIcons = null;
+ mSignalCluster = null;
+ }
+
+ public void setKeyguardShowing(boolean keyguardShowing) {
+ mKeyguardShowing = keyguardShowing;
+ if (keyguardShowing) {
+ setZ(0);
+ } else {
+ setTranslationZ(0);
+ }
+ updateHeights();
+ updateWidth();
+ updateVisibilities();
+ }
+
+ public void setUserInfoController(UserInfoController userInfoController) {
+ mMultiUserSwitch.setUserInfoController(userInfoController);
+ }
+
+ public void setOverlayParent(ViewGroup parent) {
+ mMultiUserSwitch.setOverlayParent(parent);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == mSettingsButton) {
+ startSettingsActivity();
+ }
+ }
+
+ private void startSettingsActivity() {
+ mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));
+ }
}
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 f24c1b6..48c54fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -73,8 +73,7 @@ public class StatusBarKeyguardViewManager {
public void show(Bundle options) {
mShowing = true;
mStatusBarWindowManager.setKeyguardShowing(true);
- showBouncerOrKeyguard();
- updateStates();
+ reset();
}
/**
@@ -105,13 +104,15 @@ public class StatusBarKeyguardViewManager {
* Reset the state of the view.
*/
public void reset() {
- if (mOccluded) {
- mPhoneStatusBar.hideKeyguard();
- mBouncer.hide();
- } else {
- showBouncerOrKeyguard();
+ if (mShowing) {
+ if (mOccluded) {
+ mPhoneStatusBar.hideKeyguard();
+ mBouncer.hide();
+ } else {
+ showBouncerOrKeyguard();
+ }
+ updateStates();
}
- updateStates();
}
public void onScreenTurnedOff() {
@@ -121,7 +122,6 @@ public class StatusBarKeyguardViewManager {
public void onScreenTurnedOn(final IKeyguardShowCallback callback) {
mScreenOn = true;
- reset();
if (callback != null) {
callbackAfterDraw(callback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index a4c9df5..8809d18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -78,9 +78,8 @@ public class StatusBarWindowManager {
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
PixelFormat.TRANSLUCENT);
-
mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
- mLp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ mLp.gravity = Gravity.TOP;
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 8ced1c9..55a0bba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -20,6 +20,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.TypedArray;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
@@ -30,6 +31,7 @@ import android.util.AttributeSet;
import android.widget.TextView;
import com.android.systemui.DemoMode;
+import com.android.systemui.R;
import java.text.SimpleDateFormat;
import java.util.Calendar;
@@ -52,7 +54,7 @@ public class Clock extends TextView implements DemoMode {
private static final int AM_PM_STYLE_SMALL = 1;
private static final int AM_PM_STYLE_GONE = 2;
- private static final int AM_PM_STYLE = AM_PM_STYLE_GONE;
+ private final int mAmPmStyle;
public Clock(Context context) {
this(context, null);
@@ -64,6 +66,15 @@ public class Clock extends TextView implements DemoMode {
public Clock(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ TypedArray a = context.getTheme().obtainStyledAttributes(
+ attrs,
+ R.styleable.Clock,
+ 0, 0);
+ try {
+ mAmPmStyle = a.getInt(R.styleable.Clock_amPmStyle, AM_PM_STYLE_GONE);
+ } finally {
+ a.recycle();
+ }
}
@Override
@@ -145,7 +156,7 @@ public class Clock extends TextView implements DemoMode {
* add dummy characters around it to let us find it again after
* formatting and change its size.
*/
- if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) {
+ if (mAmPmStyle != AM_PM_STYLE_NORMAL) {
int a = -1;
boolean quoted = false;
for (int i = 0; i < format.length(); i++) {
@@ -177,15 +188,15 @@ public class Clock extends TextView implements DemoMode {
}
String result = sdf.format(mCalendar.getTime());
- if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) {
+ if (mAmPmStyle != AM_PM_STYLE_NORMAL) {
int magic1 = result.indexOf(MAGIC1);
int magic2 = result.indexOf(MAGIC2);
if (magic1 >= 0 && magic2 > magic1) {
SpannableStringBuilder formatted = new SpannableStringBuilder(result);
- if (AM_PM_STYLE == AM_PM_STYLE_GONE) {
+ if (mAmPmStyle == AM_PM_STYLE_GONE) {
formatted.delete(magic1, magic2+1);
} else {
- if (AM_PM_STYLE == AM_PM_STYLE_SMALL) {
+ if (mAmPmStyle == AM_PM_STYLE_SMALL) {
CharacterStyle style = new RelativeSizeSpan(0.7f);
formatted.setSpan(style, magic1, magic2,
Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
new file mode 100644
index 0000000..173af40
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.policy;
+
+import android.app.ActivityManagerNative;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.ContactsContract;
+import android.security.KeyChain;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.view.RotationPolicy;
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public final class UserInfoController {
+
+ private static final String TAG = "UserInfoController";
+
+ private final Context mContext;
+ private final ArrayList<OnUserInfoChangedListener> mCallbacks =
+ new ArrayList<OnUserInfoChangedListener>();
+ private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask;
+
+ private boolean mUseDefaultAvatar;
+ private String mUserName;
+ private Drawable mUserDrawable;
+
+ public UserInfoController(Context context) {
+ mContext = context;
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ mContext.registerReceiver(mReceiver, filter);
+
+ IntentFilter profileFilter = new IntentFilter();
+ profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
+ profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
+ mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
+ null, null);
+ }
+
+ public void addListener(OnUserInfoChangedListener callback) {
+ mCallbacks.add(callback);
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ reloadUserInfo();
+ } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+ if (mUseDefaultAvatar) {
+ reloadUserInfo();
+ }
+ }
+ }
+ };
+
+ private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) ||
+ Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
+ try {
+ final int currentUser = ActivityManagerNative.getDefault().getCurrentUser().id;
+ final int changedUser =
+ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
+ if (changedUser == currentUser) {
+ reloadUserInfo();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't get current user id for profile change", e);
+ }
+ }
+ }
+ };
+
+ public void reloadUserInfo() {
+ if (mUserInfoTask != null) {
+ mUserInfoTask.cancel(false);
+ mUserInfoTask = null;
+ }
+ queryForUserInformation();
+ }
+
+ private Bitmap circularClip(Bitmap input) {
+ 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;
+ }
+
+ private void queryForUserInformation() {
+ Context currentUserContext;
+ UserInfo userInfo;
+ try {
+ userInfo = ActivityManagerNative.getDefault().getCurrentUser();
+ currentUserContext = mContext.createPackageContextAsUser("android", 0,
+ new UserHandle(userInfo.id));
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Couldn't create user context", e);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't get user info", e);
+ throw new RuntimeException(e);
+ }
+ final int userId = userInfo.id;
+ final String userName = userInfo.name;
+
+ final Context context = currentUserContext;
+ mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() {
+ @Override
+ protected Pair<String, Drawable> doInBackground(Void... params) {
+ final UserManager um = UserManager.get(mContext);
+
+ // Fall back to the UserManager nickname if we can't read the name from the local
+ // profile below.
+ String name = userName;
+ Drawable avatar = null;
+ Bitmap rawAvatar = um.getUserIcon(userId);
+ if (rawAvatar != null) {
+ avatar = new BitmapDrawable(mContext.getResources(), circularClip(rawAvatar));
+ } else {
+ avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user);
+ mUseDefaultAvatar = true;
+ }
+
+ // If it's a single-user device, get the profile name, since the nickname is not
+ // usually valid
+ if (um.getUsers().size() <= 1) {
+ // Try and read the display name from the local profile
+ final Cursor cursor = context.getContentResolver().query(
+ ContactsContract.Profile.CONTENT_URI, new String[] {
+ ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME},
+ null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ name = cursor.getString(cursor.getColumnIndex(
+ ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+ return new Pair<String, Drawable>(name, avatar);
+ }
+
+ @Override
+ protected void onPostExecute(Pair<String, Drawable> result) {
+ mUserName = result.first;
+ mUserDrawable = result.second;
+ mUserInfoTask = null;
+ notifyChanged();
+ }
+ };
+ mUserInfoTask.execute();
+ }
+
+ private void notifyChanged() {
+ for (OnUserInfoChangedListener listener : mCallbacks) {
+ listener.onUserInfoChanged(mUserName, mUserDrawable);
+ }
+ }
+
+ public interface OnUserInfoChangedListener {
+ public void onUserInfoChanged(String name, Drawable picture);
+ }
+}
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 afd5068..5849afb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -80,9 +80,12 @@ public class NotificationStackScrollLayout extends ViewGroup
private Paint mDebugPaint;
private int mContentHeight;
private int mCollapsedSize;
+ private int mBottomStackSlowDownHeight;
private int mBottomStackPeekSize;
private int mEmptyMarginBottom;
private int mPaddingBetweenElements;
+ private int mPaddingBetweenElementsDimmed;
+ private int mPaddingBetweenElementsNormal;
private int mTopPadding;
/**
@@ -153,7 +156,10 @@ public class NotificationStackScrollLayout extends ViewGroup
if (DEBUG) {
int y = mCollapsedSize;
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
- y = (int) (getLayoutHeight() - mBottomStackPeekSize - mCollapsedSize);
+ y = (int) (getLayoutHeight() - mBottomStackPeekSize
+ - mBottomStackSlowDownHeight);
+ canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+ y = (int) (getLayoutHeight() - mBottomStackPeekSize);
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
y = (int) getLayoutHeight();
canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -183,9 +189,20 @@ public class NotificationStackScrollLayout extends ViewGroup
.getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
mEmptyMarginBottom = context.getResources().getDimensionPixelSize(
R.dimen.notification_stack_margin_bottom);
- mPaddingBetweenElements = context.getResources()
- .getDimensionPixelSize(R.dimen.notification_padding);
mStackScrollAlgorithm = new StackScrollAlgorithm(context);
+ mPaddingBetweenElementsDimmed = context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_padding_dimmed);
+ mPaddingBetweenElementsNormal = context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_padding);
+ updatePadding(false);
+ }
+
+ private void updatePadding(boolean dimmed) {
+ mPaddingBetweenElements = dimmed
+ ? mPaddingBetweenElementsDimmed
+ : mPaddingBetweenElementsNormal;
+ mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength();
+ updateContentHeight();
}
@Override
@@ -739,15 +756,10 @@ public class NotificationStackScrollLayout extends ViewGroup
if (firstChild != null) {
int contentHeight = getContentHeight();
int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild);
-
- scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize);
+ scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize
+ + mBottomStackSlowDownHeight);
if (scrollRange > 0) {
View lastChild = getLastChildNotGone();
- if (isViewExpanded(lastChild)) {
- // last child is expanded, so we have to ensure that it can exit the
- // bottom stack
- scrollRange += mCollapsedSize + mPaddingBetweenElements;
- }
// We want to at least be able collapse the first item and not ending in a weird
// end state.
scrollRange = Math.max(scrollRange, firstChildMaxExpandHeight - mCollapsedSize);
@@ -1181,7 +1193,7 @@ public class NotificationStackScrollLayout extends ViewGroup
public int getEmptyBottomMargin() {
int emptyMargin = mMaxLayoutHeight - mContentHeight;
if (needsHeightAdaption()) {
- emptyMargin = emptyMargin - mCollapsedSize - mBottomStackPeekSize;
+ emptyMargin = emptyMargin - mBottomStackSlowDownHeight - mBottomStackPeekSize;
}
return Math.max(emptyMargin, 0);
}
@@ -1226,7 +1238,9 @@ public class NotificationStackScrollLayout extends ViewGroup
* See {@link AmbientState#setDimmed}.
*/
public void setDimmed(boolean dimmed, boolean animate) {
+ mStackScrollAlgorithm.setDimmed(dimmed);
mAmbientState.setDimmed(dimmed);
+ updatePadding(dimmed);
if (animate) {
mDimmedNeedsAnimation = true;
mNeedsAnimation = true;
@@ -1311,6 +1325,7 @@ public class NotificationStackScrollLayout extends ViewGroup
// ANIMATION_TYPE_DIMMED
new AnimationFilter()
+ .animateY()
.animateScale()
.animateDimmed()
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java
index 38b544f..1c37c35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java
@@ -38,27 +38,26 @@ public class PiecewiseLinearIndentationFunctor extends StackIndentationFunctor {
* the actual visual distance below the top card but is a maximum,
* achieved when the next card just starts transitioning into the stack and
* the stack is full.
- * If totalTransitionDistance is equal to this, we directly start at the peek,
- * otherwise the first element transitions between 0 and
- * totalTransitionDistance - peekSize.
+ * If distanceToPeekStart is 0, we directly start at the peek, otherwise the
+ * first element transitions between 0 and distanceToPeekStart.
* Visualization:
* --------------------------------------------------- ---
* | | |
- * | FIRST ITEM | | <- totalTransitionDistance
+ * | FIRST ITEM | | <- distanceToPeekStart
* | | |
- * |---------------------------------------------------| | ---
- * |__________________SECOND ITEM______________________| | | <- peekSize
- * |===================================================| _|_ _|_
+ * |---------------------------------------------------| --- ---
+ * |__________________SECOND ITEM______________________| | <- peekSize
+ * |===================================================| _|_
*
- * @param totalTransitionDistance The total transition distance an element has to go through
+ * @param distanceToPeekStart The distance to the start of the peak.
* @param linearPart The interpolation factor between the linear and the quadratic amount taken.
* This factor must be somewhere in [0 , 1]
*/
PiecewiseLinearIndentationFunctor(int maxItemsInStack,
int peekSize,
- int totalTransitionDistance,
+ int distanceToPeekStart,
float linearPart) {
- super(maxItemsInStack, peekSize, totalTransitionDistance);
+ super(maxItemsInStack, peekSize, distanceToPeekStart);
mBaseValues = new ArrayList<Float>(maxItemsInStack+1);
initBaseValues();
mLinearPart = linearPart;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java
index f72947a..034eba6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java
@@ -21,8 +21,8 @@ package com.android.systemui.statusbar.stack;
*/
public abstract class StackIndentationFunctor {
- protected final int mTotalTransitionDistance;
- protected final int mDistanceToPeekStart;
+ protected int mTotalTransitionDistance;
+ protected int mDistanceToPeekStart;
protected int mMaxItemsInStack;
protected int mPeekSize;
protected boolean mStackStartsAtPeek;
@@ -37,31 +37,41 @@ public abstract class StackIndentationFunctor {
* the actual visual distance below the top card but is a maximum,
* achieved when the next card just starts transitioning into the stack and
* the stack is full.
- * If totalTransitionDistance is equal to this, we directly start at the peek,
- * otherwise the first element transitions between 0 and
- * totalTransitionDistance - peekSize.
+ * If distanceToPeekStart is 0, we directly start at the peek, otherwise the
+ * first element transitions between 0 and distanceToPeekStart.
* Visualization:
* --------------------------------------------------- ---
* | | |
- * | FIRST ITEM | | <- totalTransitionDistance
+ * | FIRST ITEM | | <- distanceToPeekStart
* | | |
- * |---------------------------------------------------| | ---
- * |__________________SECOND ITEM______________________| | | <- peekSize
- * |===================================================| _|_ _|_
+ * |---------------------------------------------------| --- ---
+ * |__________________SECOND ITEM______________________| | <- peekSize
+ * |===================================================| _|_
*
- * @param totalTransitionDistance The total transition distance an element has to go through
+ * @param distanceToPeekStart The distance to the start of the peak.
*/
- StackIndentationFunctor(int maxItemsInStack, int peekSize, int totalTransitionDistance) {
- mTotalTransitionDistance = totalTransitionDistance;
- mDistanceToPeekStart = mTotalTransitionDistance - peekSize;
+ StackIndentationFunctor(int maxItemsInStack, int peekSize, int distanceToPeekStart) {
+ mDistanceToPeekStart = distanceToPeekStart;
mStackStartsAtPeek = mDistanceToPeekStart == 0;
mMaxItemsInStack = maxItemsInStack;
mPeekSize = peekSize;
+ updateTotalTransitionDistance();
}
+ private void updateTotalTransitionDistance() {
+ mTotalTransitionDistance = mDistanceToPeekStart + mPeekSize;
+ }
+
public void setPeekSize(int mPeekSize) {
this.mPeekSize = mPeekSize;
+ updateTotalTransitionDistance();
+ }
+
+ public void setDistanceToPeekStart(int distanceToPeekStart) {
+ mDistanceToPeekStart = distanceToPeekStart;
+ mStackStartsAtPeek = mDistanceToPeekStart == 0;
+ updateTotalTransitionDistance();
}
/**
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 5e4d496..bd9de82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -65,13 +65,40 @@ public class StackScrollAlgorithm {
private ExpandableView mFirstChildWhileExpanding;
private boolean mExpandedOnStart;
private int mTopStackTotalSize;
+ private int mPaddingBetweenElementsDimmed;
+ private int mPaddingBetweenElementsNormal;
+ private int mBottomStackSlowDownLength;
public StackScrollAlgorithm(Context context) {
initConstants(context);
+ updatePadding(false);
+ }
+
+ private void updatePadding(boolean dimmed) {
+ mPaddingBetweenElements = dimmed
+ ? mPaddingBetweenElementsDimmed
+ : mPaddingBetweenElementsNormal;
+ mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements;
+ mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
+ MAX_ITEMS_IN_TOP_STACK,
+ mTopStackPeekSize,
+ mTopStackTotalSize - mTopStackPeekSize,
+ 0.5f);
+ mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
+ MAX_ITEMS_IN_BOTTOM_STACK,
+ mBottomStackPeekSize,
+ getBottomStackSlowDownLength(),
+ 0.5f);
+ }
+
+ public int getBottomStackSlowDownLength() {
+ return mBottomStackSlowDownLength + mPaddingBetweenElements;
}
private void initConstants(Context context) {
- mPaddingBetweenElements = context.getResources()
+ mPaddingBetweenElementsDimmed = context.getResources()
+ .getDimensionPixelSize(R.dimen.notification_padding_dimmed);
+ mPaddingBetweenElementsNormal = context.getResources()
.getDimensionPixelSize(R.dimen.notification_padding);
mCollapsedSize = context.getResources()
.getDimensionPixelSize(R.dimen.notification_min_height);
@@ -82,17 +109,8 @@ public class StackScrollAlgorithm {
mZDistanceBetweenElements = context.getResources()
.getDimensionPixelSize(R.dimen.z_distance_between_notifications);
mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
- mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements;
- mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
- MAX_ITEMS_IN_TOP_STACK,
- mTopStackPeekSize,
- mTopStackTotalSize,
- 0.5f);
- mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
- MAX_ITEMS_IN_BOTTOM_STACK,
- mBottomStackPeekSize,
- mCollapsedSize + mBottomStackPeekSize + mPaddingBetweenElements,
- 0.5f);
+ mBottomStackSlowDownLength = context.getResources()
+ .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
}
@@ -206,7 +224,7 @@ public class StackScrollAlgorithm {
float bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
// The position where the bottom stack starts.
- float bottomStackStart = bottomPeekStart - mCollapsedSize;
+ float bottomStackStart = bottomPeekStart - mBottomStackSlowDownLength;
// The y coordinate of the current child.
float currentYPosition = 0.0f;
@@ -352,7 +370,7 @@ public class StackScrollAlgorithm {
algorithmState.itemsInBottomStack += algorithmState.partialInBottom;
childViewState.yTranslation = transitioningPositionStart + offset - childHeight
- mPaddingBetweenElements;
-
+
// We want at least to be at the end of the top stack when collapsing
clampPositionToTopStackEnd(childViewState, childHeight);
childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA;
@@ -621,6 +639,10 @@ public class StackScrollAlgorithm {
}
}
+ public void setDimmed(boolean dimmed) {
+ updatePadding(dimmed);
+ }
+
class StackScrollAlgorithmState {
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3619112..4a59a8f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -38,6 +38,7 @@ import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.graphics.Point;
import android.graphics.Rect;
@@ -197,6 +198,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
+ private final UserManager mUserManager;
+
private int mCurrentUserId = UserHandle.USER_OWNER;
private final LongArray mTempLongArray = new LongArray();
@@ -210,15 +213,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
return getUserStateLocked(mCurrentUserId);
}
- private UserState getUserStateLocked(int userId) {
- UserState state = mUserStates.get(userId);
- if (state == null) {
- state = new UserState(userId);
- mUserStates.put(userId, state);
- }
- return state;
- }
-
/**
* Creates a new instance.
*
@@ -228,6 +222,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
mContext = context;
mPackageManager = mContext.getPackageManager();
mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mSecurityPolicy = new SecurityPolicy();
mMainHandler = new MainHandler(mContext.getMainLooper());
registerBroadcastReceivers();
@@ -235,11 +230,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
context.getContentResolver());
}
+ private UserState getUserStateLocked(int userId) {
+ UserState state = mUserStates.get(userId);
+ if (state == null) {
+ state = new UserState(userId);
+ mUserStates.put(userId, state);
+ }
+ return state;
+ }
+
private void registerBroadcastReceivers() {
PackageMonitor monitor = new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
synchronized (mLock) {
+ // Only the profile parent can install accessibility services.
+ // Therefore we ignore packages from linked profiles.
if (getChangingUserId() != mCurrentUserId) {
return;
}
@@ -262,6 +268,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
final int userId = getChangingUserId();
+ // Only the profile parent can install accessibility services.
+ // Therefore we ignore packages from linked profiles.
if (userId != mCurrentUserId) {
return;
}
@@ -297,6 +305,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
int uid, boolean doit) {
synchronized (mLock) {
final int userId = getChangingUserId();
+ // Only the profile parent can install accessibility services.
+ // Therefore we ignore packages from linked profiles.
if (userId != mCurrentUserId) {
return false;
}
@@ -359,6 +369,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
@Override
public int addClient(IAccessibilityManagerClient client, int userId) {
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// If the client is from a process that runs across users such as
@@ -388,6 +401,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
@Override
public boolean sendAccessibilityEvent(AccessibilityEvent event, int userId) {
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution..
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// This method does nothing for a background user.
@@ -414,6 +430,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
@Override
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// The automation service is a fake one and should not be reported
@@ -435,6 +454,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
int userId) {
List<AccessibilityServiceInfo> result = null;
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
@@ -468,6 +490,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public void interrupt(int userId) {
CopyOnWriteArrayList<Service> services;
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// This method does nothing for a background user.
@@ -491,6 +516,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public int addAccessibilityInteractionConnection(IWindow windowToken,
IAccessibilityInteractionConnection connection, int userId) throws RemoteException {
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
final int windowId = sNextWindowId++;
@@ -527,6 +555,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
@Override
public void removeAccessibilityInteractionConnection(IWindow window) {
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
UserHandle.getCallingUserId());
IBinder token = window.asBinder();
@@ -675,6 +706,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
Manifest.permission.RETRIEVE_WINDOW_TOKEN,
GET_WINDOW_TOKEN);
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(
UserHandle.getCallingUserId());
@@ -770,7 +804,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
// Disconnect from services for the old user.
- UserState oldUserState = getUserStateLocked(mCurrentUserId);
+ UserState oldUserState = getCurrentUserStateLocked();
oldUserState.onSwitchToAnotherUser();
// Disable the local managers for the old user.
@@ -2034,6 +2068,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
@Override
public List<AccessibilityWindowInfo> getWindows() {
synchronized (mLock) {
+ // We treat calls from a profile as if made by its perent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(
UserHandle.getCallingUserId());
@@ -2062,6 +2099,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
@Override
public AccessibilityWindowInfo getWindow(int windowId) {
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(
UserHandle.getCallingUserId());
@@ -2092,6 +2132,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(
UserHandle.getCallingUserId());
@@ -2136,9 +2179,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId());
if (resolvedUserId != mCurrentUserId) {
return false;
}
@@ -2180,9 +2226,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId());
if (resolvedUserId != mCurrentUserId) {
return false;
}
@@ -2224,9 +2273,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId());
if (resolvedUserId != mCurrentUserId) {
return false;
}
@@ -2268,9 +2320,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId());
if (resolvedUserId != mCurrentUserId) {
return false;
}
@@ -2311,9 +2366,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId());
if (resolvedUserId != mCurrentUserId) {
return false;
}
@@ -2346,9 +2404,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public boolean performGlobalAction(int action) {
synchronized (mLock) {
+ // We treat calls from a profile as if made by its parent as profiles
+ // share the accessibility state of the parent. The call below
+ // performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(
- UserHandle.getCallingUserId());
+ UserHandle.getCallingUserId());
if (resolvedUserId != mCurrentUserId) {
return false;
}
@@ -3407,16 +3468,35 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
& AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
}
+ private int resolveProfileParentLocked(int userId) {
+ if (userId != mCurrentUserId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ UserInfo parent = mUserManager.getProfileParent(userId);
+ if (parent != null) {
+ return parent.getUserHandle().getIdentifier();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ return userId;
+ }
+
public int resolveCallingUserIdEnforcingPermissionsLocked(int userId) {
final int callingUid = Binder.getCallingUid();
if (callingUid == 0
|| callingUid == Process.SYSTEM_UID
|| callingUid == Process.SHELL_UID) {
- return mCurrentUserId;
+ if (userId == UserHandle.USER_CURRENT
+ || userId == UserHandle.USER_CURRENT_OR_SELF) {
+ return mCurrentUserId;
+ }
+ return resolveProfileParentLocked(userId);
}
final int callingUserId = UserHandle.getUserId(callingUid);
if (callingUserId == userId) {
- return userId;
+ return resolveProfileParentLocked(userId);
}
if (!hasPermission(Manifest.permission.INTERACT_ACROSS_USERS)
&& !hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
@@ -3673,8 +3753,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
public void onChange(boolean selfChange, Uri uri) {
if (mAccessibilityEnabledUri.equals(uri)) {
synchronized (mLock) {
- // We will update when the automation service dies.
+ // Profiles share the accessibility state of the parent. Therefore,
+ // we are checking for changes only the parent settings.
UserState userState = getCurrentUserStateLocked();
+ // We will update when the automation service dies.
if (userState.mUiAutomationService == null) {
if (readAccessibilityEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
@@ -3683,8 +3765,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
} else if (mTouchExplorationEnabledUri.equals(uri)) {
synchronized (mLock) {
- // We will update when the automation service dies.
+ // Profiles share the accessibility state of the parent. Therefore,
+ // we are checking for changes only the parent settings.
UserState userState = getCurrentUserStateLocked();
+ // We will update when the automation service dies.
if (userState.mUiAutomationService == null) {
if (readTouchExplorationEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
@@ -3693,8 +3777,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
} else if (mDisplayMagnificationEnabledUri.equals(uri)) {
synchronized (mLock) {
- // We will update when the automation service dies.
+ // Profiles share the accessibility state of the parent. Therefore,
+ // we are checking for changes only the parent settings.
UserState userState = getCurrentUserStateLocked();
+ // We will update when the automation service dies.
if (userState.mUiAutomationService == null) {
if (readDisplayMagnificationEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
@@ -3703,8 +3789,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
} else if (mEnabledAccessibilityServicesUri.equals(uri)) {
synchronized (mLock) {
- // We will update when the automation service dies.
+ // Profiles share the accessibility state of the parent. Therefore,
+ // we are checking for changes only the parent settings.
UserState userState = getCurrentUserStateLocked();
+ // We will update when the automation service dies.
if (userState.mUiAutomationService == null) {
if (readEnabledAccessibilityServicesLocked(userState)) {
onUserStateChangedLocked(userState);
@@ -3713,8 +3801,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
} else if (mTouchExplorationGrantedAccessibilityServicesUri.equals(uri)) {
synchronized (mLock) {
- // We will update when the automation service dies.
+ // Profiles share the accessibility state of the parent. Therefore,
+ // we are checking for changes only the parent settings.
UserState userState = getCurrentUserStateLocked();
+ // We will update when the automation service dies.
if (userState.mUiAutomationService == null) {
if (readTouchExplorationGrantedAccessibilityServicesLocked(userState)) {
onUserStateChangedLocked(userState);
@@ -3723,8 +3813,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
}
} else if (mEnhancedWebAccessibilityUri.equals(uri)) {
synchronized (mLock) {
- // We will update when the automation service dies.
+ // Profiles share the accessibility state of the parent. Therefore,
+ // we are checking for changes only the parent settings.
UserState userState = getCurrentUserStateLocked();
+ // We will update when the automation service dies.
if (userState.mUiAutomationService == null) {
if (readEnhancedWebAccessibilityEnabledChangedLocked(userState)) {
onUserStateChangedLocked(userState);
@@ -3739,8 +3831,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
|| mDisplayInversionUri.equals(uri)
|| mDisplayDaltonizerUri.equals(uri)) {
synchronized (mLock) {
- // We will update when the automation service dies.
+ // Profiles share the accessibility state of the parent. Therefore,
+ // we are checking for changes only the parent settings.
UserState userState = getCurrentUserStateLocked();
+ // We will update when the automation service dies.
if (userState.mUiAutomationService == null) {
if (readDisplayColorAdjustmentSettingsLocked(userState)) {
updateDisplayColorAdjustmentSettingsLocked(userState);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index dfffa8a..4ea33db 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -65,6 +65,7 @@ import android.net.LinkProperties;
import android.net.LinkProperties.CompareResult;
import android.net.LinkQualityInfo;
import android.net.MobileDataStateTracker;
+import android.net.Network;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
@@ -165,6 +166,8 @@ import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
+import static android.net.ConnectivityManager.INVALID_NET_ID;
+
/**
* @hide
*/
@@ -442,6 +445,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
TelephonyManager mTelephonyManager;
+ private final static int MIN_NET_ID = 10; // some reserved marks
+ private final static int MAX_NET_ID = 65535;
+ private int mNextNetId = MIN_NET_ID;
+
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
// Currently, omitting a NetworkFactory will create one internally
@@ -706,6 +713,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
}
+ private synchronized int nextNetId() {
+ int netId = mNextNetId;
+ if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
+ return netId;
+ }
+
/**
* Factory that creates {@link NetworkStateTracker} instances using given
* {@link NetworkConfig}.
@@ -1984,6 +1997,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
int prevNetType = info.getType();
mNetTrackers[prevNetType].setTeardownRequested(false);
+ int thisNetId = mNetTrackers[prevNetType].getNetwork().netId;
// Remove idletimer previously setup in {@code handleConnect}
if (mNetConfigs[prevNetType].isDefault()) {
@@ -2069,6 +2083,13 @@ public class ConnectivityService extends IConnectivityManager.Stub {
sendConnectedBroadcastDelayed(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(),
getConnectivityChangeDelay());
}
+ try {
+ mNetd.removeNetwork(thisNetId);
+ } catch (Exception e) {
+ loge("Exception removing network: " + e);
+ } finally {
+ mNetTrackers[prevNetType].setNetId(INVALID_NET_ID);
+ }
}
private void tryFailover(int prevNetType) {
@@ -2336,17 +2357,23 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (mNetConfigs[newNetType].isDefault()) {
if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) {
if (isNewNetTypePreferredOverCurrentNetType(newNetType)) {
- // tear down the other
- NetworkStateTracker otherNet =
- mNetTrackers[mActiveDefaultNetwork];
- if (DBG) {
- log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
- " teardown");
- }
- if (!teardown(otherNet)) {
- loge("Network declined teardown request");
- teardown(thisNet);
- return;
+ String teardownPolicy = SystemProperties.get("net.teardownPolicy");
+ if (TextUtils.equals(teardownPolicy, "keep") == false) {
+ // tear down the other
+ NetworkStateTracker otherNet =
+ mNetTrackers[mActiveDefaultNetwork];
+ if (DBG) {
+ log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
+ " teardown");
+ }
+ if (!teardown(otherNet)) {
+ loge("Network declined teardown request");
+ teardown(thisNet);
+ return;
+ }
+ } else {
+ //TODO - remove
+ loge("network teardown skipped due to net.teardownPolicy setting");
}
} else {
// don't accept this one
@@ -2358,6 +2385,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return;
}
}
+ int thisNetId = nextNetId();
+ thisNet.setNetId(thisNetId);
+ try {
+ mNetd.createNetwork(thisNetId, thisIface);
+ } catch (Exception e) {
+ loge("Exception creating network :" + e);
+ teardown(thisNet);
+ return;
+ }
setupDataActivityTracking(newNetType);
synchronized (ConnectivityService.this) {
// have a new default network, release the transition wakelock in a second
@@ -2380,6 +2416,16 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// Don't do this - if we never sign in stay, grey
//reportNetworkCondition(mActiveDefaultNetwork, 100);
updateNetworkSettings(thisNet);
+ } else {
+ int thisNetId = nextNetId();
+ thisNet.setNetId(thisNetId);
+ try {
+ mNetd.createNetwork(thisNetId, thisIface);
+ } catch (Exception e) {
+ loge("Exception creating network :" + e);
+ teardown(thisNet);
+ return;
+ }
}
thisNet.setTeardownRequested(false);
updateMtuSizeSettings(thisNet);
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index 0d1e122..96f9ab0 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -50,6 +50,8 @@ import java.util.LinkedList;
final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
private static final boolean LOGD = false;
+ private final static boolean VDBG = false;
+
private final String TAG;
private String mSocket;
@@ -409,7 +411,7 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
loge("timed-out waiting for response to " + logCmd);
throw new NativeDaemonFailureException(logCmd, event);
}
- log("RMV <- {" + event + "}");
+ if (VDBG) log("RMV <- {" + event + "}");
events.add(event);
} while (event.isClassContinue());
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 7ce45f7..b9c86dc 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2029,4 +2029,24 @@ public class NetworkManagementService extends INetworkManagementService.Stub
pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
}
+
+ public void createNetwork(int netId, String iface) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ try {
+ mConnector.execute("network", "create", netId, iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ public void removeNetwork(int netId) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ try {
+ mConnector.execute("network", "destroy", netId);
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 4f0c9b5..512ebc6 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -171,6 +171,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub {
return;
}
writer.println("Current scorer: " + currentScorer);
+ writer.flush();
for (INetworkScoreCache scoreCache : getScoreCaches()) {
try {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 132ca00..82c13e0 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -41,6 +41,7 @@ import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Slog;
import android.view.InputDevice;
+import android.media.AudioManager;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
@@ -73,6 +74,8 @@ public class VibratorService extends IVibratorService.Stub
private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
private int mCurVibUid = -1;
+ private boolean mLowPowerMode;
+ private SettingsObserver mSettingObserver;
native static boolean vibratorExists();
native static void vibratorOn(long milliseconds);
@@ -159,15 +162,15 @@ public class VibratorService extends IVibratorService.Stub
public void systemReady() {
mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE);
+ mSettingObserver = new SettingsObserver(mH);
mContext.getContentResolver().registerContentObserver(
- Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), true,
- new ContentObserver(mH) {
- @Override
- public void onChange(boolean selfChange) {
- updateInputDeviceVibrators();
- }
- }, UserHandle.USER_ALL);
+ Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
+ true, mSettingObserver, UserHandle.USER_ALL);
+
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE), false,
+ mSettingObserver, UserHandle.USER_ALL);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
@@ -179,6 +182,17 @@ public class VibratorService extends IVibratorService.Stub
updateInputDeviceVibrators();
}
+ private final class SettingsObserver extends ContentObserver {
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean SelfChange) {
+ updateInputDeviceVibrators();
+ }
+ }
+
public boolean hasVibrator() {
return doVibratorExists();
}
@@ -346,6 +360,10 @@ public class VibratorService extends IVibratorService.Stub
// Lock held on mVibrations
private void startVibrationLocked(final Vibration vib) {
try {
+ if (mLowPowerMode && vib.mStreamHint != AudioManager.STREAM_RING) {
+ return;
+ }
+
int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
vib.mStreamHint, vib.mUid, vib.mOpPkg);
if (mode == AppOpsManager.MODE_ALLOWED) {
@@ -425,6 +443,9 @@ public class VibratorService extends IVibratorService.Stub
} catch (SettingNotFoundException snfe) {
}
+ mLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.LOW_POWER_MODE, 0) != 0;
+
if (mVibrateInputDevicesSetting) {
if (!mInputDeviceListenerRegistered) {
mInputDeviceListenerRegistered = true;
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index 50cfe48..c32beda 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -70,6 +70,7 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
private static final String NAME_HDMI = "hdmi";
private static final int MSG_NEW_DEVICE_STATE = 1;
+ private static final int MSG_SYSTEM_READY = 2;
private final Object mLock = new Object();
@@ -96,19 +97,9 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
mObserver = new WiredAccessoryObserver();
-
- IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
- filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- context.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context ctx, Intent intent) {
- bootCompleted();
- }
- },
- filter, null, null);
}
- private void bootCompleted() {
+ private void onSystemReady() {
if (mUseDevInputEventForAudioJack) {
int switchValues = 0;
if (mInputManager.getSwitchState(-1, InputDevice.SOURCE_ANY, SW_HEADPHONE_INSERT) == 1) {
@@ -159,6 +150,16 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
}
}
+ @Override
+ public void systemReady() {
+ synchronized (mLock) {
+ mWakeLock.acquire();
+
+ Message msg = mHandler.obtainMessage(MSG_SYSTEM_READY, 0, 0, null);
+ mHandler.sendMessage(msg);
+ }
+ }
+
/**
* Compare the existing headset state with the new state and pass along accordingly. Note
* that this only supports a single headset at a time. Inserting both a usb and jacked headset
@@ -220,6 +221,11 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
case MSG_NEW_DEVICE_STATE:
setDevicesState(msg.arg1, msg.arg2, (String)msg.obj);
mWakeLock.release();
+ break;
+ case MSG_SYSTEM_READY:
+ onSystemReady();
+ mWakeLock.release();
+ break;
}
}
};
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index efc5606..5358c1a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -35,6 +35,7 @@ import android.app.IActivityContainerCallback;
import android.appwidget.AppWidgetManager;
import android.graphics.Rect;
import android.os.BatteryStats;
+import android.os.PersistableBundle;
import android.service.voice.IVoiceInteractionSession;
import android.util.ArrayMap;
@@ -1327,12 +1328,14 @@ public final class ActivityManagerService extends ActivityManagerNative
String host = "";
String port = "";
String exclList = "";
- String pacFileUrl = null;
+ String pacFileUrl = "";
if (proxy != null) {
host = proxy.getHost();
port = Integer.toString(proxy.getPort());
exclList = proxy.getExclusionListAsString();
- pacFileUrl = proxy.getPacFileUrl().toString();
+ if (proxy.getPacFileUrl() != null) {
+ pacFileUrl = proxy.getPacFileUrl().toString();
+ }
}
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -5427,22 +5430,21 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
- public final void activityPaused(IBinder token) {
+ public final void activityPaused(IBinder token, PersistableBundle persistentState) {
final long origId = Binder.clearCallingIdentity();
synchronized(this) {
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
- stack.activityPausedLocked(token, false);
+ stack.activityPausedLocked(token, false, persistentState);
}
}
Binder.restoreCallingIdentity(origId);
}
@Override
- public final void activityStopped(IBinder token, Bundle icicle, Bitmap thumbnail,
- CharSequence description) {
- if (localLOGV) Slog.v(
- TAG, "Activity stopped: token=" + token);
+ public final void activityStopped(IBinder token, Bundle icicle,
+ PersistableBundle persistentState, CharSequence description) {
+ if (localLOGV) Slog.v(TAG, "Activity stopped: token=" + token);
// Refuse possible leaked file descriptors
if (icicle != null && icicle.hasFileDescriptors()) {
@@ -5454,7 +5456,7 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (this) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r != null) {
- r.task.stack.activityStoppedLocked(r, icicle, thumbnail, description);
+ r.task.stack.activityStoppedLocked(r, icicle, persistentState, description);
}
}
@@ -9570,6 +9572,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
mAppOpsService.systemReady();
+ mUsageStatsService.systemReady();
mSystemReady = true;
}
@@ -14391,6 +14394,7 @@ public final class ActivityManagerService extends ActivityManagerNative
newConfig.seq = mConfigurationSeq;
mConfiguration = newConfig;
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + newConfig);
+ mUsageStatsService.noteStartConfig(newConfig);
final Configuration configCopy = new Configuration(mConfiguration);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index efd2b57..8391f79 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.os.PersistableBundle;
import android.os.Trace;
import com.android.internal.app.ResolverActivity;
import com.android.server.AttributeCache;
@@ -117,6 +118,7 @@ final class ActivityRecord {
ProcessRecord app; // if non-null, hosting application
ActivityState state; // current state we are in
Bundle icicle; // last saved activity state
+ PersistableBundle persistentState; // last persistently saved activity state
boolean frontOfTask; // is this the root activity of its task?
boolean launchFailed; // set if a launched failed, to abort on 2nd try
boolean haveState; // have we gotten the last activity state?
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 442da31..7c29d85 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -68,6 +68,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -276,7 +277,7 @@ final class ActivityStack {
if (r.app != null) {
mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
}
- activityPausedLocked(r.appToken, true);
+ activityPausedLocked(r.appToken, true, r.persistentState);
}
} break;
case LAUNCH_TICK_MSG: {
@@ -860,13 +861,15 @@ final class ActivityStack {
}
}
- final void activityPausedLocked(IBinder token, boolean timeout) {
+ final void activityPausedLocked(IBinder token, boolean timeout,
+ PersistableBundle persistentState) {
if (DEBUG_PAUSE) Slog.v(
TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
final ActivityRecord r = isInStackLocked(token);
if (r != null) {
mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+ r.persistentState = persistentState;
if (mPausingActivity == r) {
if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
+ (timeout ? " (due to timeout)" : " (pause complete)"));
@@ -881,13 +884,14 @@ final class ActivityStack {
}
}
- final void activityStoppedLocked(ActivityRecord r, Bundle icicle, Bitmap thumbnail,
- CharSequence description) {
+ final void activityStoppedLocked(ActivityRecord r, Bundle icicle,
+ PersistableBundle persistentState, CharSequence description) {
if (r.state != ActivityState.STOPPING) {
Slog.i(TAG, "Activity reported stop, but no longer stopping: " + r);
mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
return;
}
+ r.persistentState = persistentState;
if (DEBUG_SAVED_STATE) Slog.i(TAG, "Saving icicle of " + r + ": " + icicle);
if (icicle != null) {
// If icicle is null, this is happening due to a timeout, so we
@@ -895,7 +899,7 @@ final class ActivityStack {
r.icicle = icicle;
r.haveState = true;
r.launchCount = 0;
- r.updateThumbnail(thumbnail, description);
+ r.updateThumbnail(null, description);
}
if (!r.stopped) {
if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r + " (stop complete)");
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index ce3d853..6f62a03 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1026,10 +1026,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
r.clearOptionsLocked();
app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
System.identityHashCode(r), r.info,
- new Configuration(mService.mConfiguration), r.compat,
- r.task.voiceInteractor, app.repProcState, r.icicle, results, newIntents,
- !andResume, mService.isNextTransitionForward(), profileFile, profileFd,
- profileAutoStop, options);
+ new Configuration(mService.mConfiguration), r.compat, r.task.voiceInteractor,
+ app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume,
+ mService.isNextTransitionForward(), profileFile, profileFd, profileAutoStop,
+ options);
if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
// This may be a heavy-weight process! Note that the package
diff --git a/services/core/java/com/android/server/am/UsageStatsService.java b/services/core/java/com/android/server/am/UsageStatsService.java
index 42cf900..4a5a554 100644
--- a/services/core/java/com/android/server/am/UsageStatsService.java
+++ b/services/core/java/com/android/server/am/UsageStatsService.java
@@ -17,26 +17,31 @@
package com.android.server.am;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.UsageStats;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.os.Binder;
import android.os.IBinder;
import android.os.FileUtils;
import android.os.Parcel;
+import android.os.ParcelableParcel;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
+import android.util.TimeUtils;
import android.util.Xml;
import com.android.internal.app.IUsageStats;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.PkgUsageStats;
import com.android.internal.util.FastXmlSerializer;
import org.xmlpull.v1.XmlPullParser;
@@ -46,7 +51,6 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -55,8 +59,6 @@ import java.util.Calendar;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -75,7 +77,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
private static final String TAG = "UsageStats";
// Current on-disk Parcel version
- private static final int VERSION = 1008;
+ private static final int VERSION = 1010;
private static final int CHECKIN_VERSION = 4;
@@ -94,13 +96,10 @@ public final class UsageStatsService extends IUsageStats.Stub {
static IUsageStats sService;
private Context mContext;
- // structure used to maintain statistics since the last checkin.
- final private ArrayMap<String, PkgUsageStatsExtended> mStats
- = new ArrayMap<String, PkgUsageStatsExtended>();
+ private AppOpsManager mAppOps;
- // Maintains the last time any component was resumed, for all time.
- final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes
- = new ArrayMap<String, ArrayMap<String, Long>>();
+ // structure used to maintain statistics since the last checkin.
+ private LocalUsageStats mStats = new LocalUsageStats();
// To remove last-resume time stats when a pacakge is removed.
private PackageMonitor mPackageMonitor;
@@ -115,6 +114,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
private String mLastResumedPkg;
private String mLastResumedComp;
private boolean mIsResumed;
+ private ConfigUsageStatsExtended mCurrentConfigStats;
private File mFile;
private AtomicFile mHistoryFile;
private String mFileLeaf;
@@ -127,6 +127,30 @@ public final class UsageStatsService extends IUsageStats.Stub {
private final AtomicLong mLastWriteElapsedTime = new AtomicLong(0);
private final AtomicBoolean mUnforcedDiskWriteRunning = new AtomicBoolean(false);
+ static class LocalUsageStats extends UsageStats {
+ public LocalUsageStats() {
+ }
+ public LocalUsageStats(Parcel in, boolean extended) {
+ super(in, extended);
+ }
+ @Override
+ public PackageStats onNewPackageStats(String pkgName) {
+ return new PkgUsageStatsExtended(pkgName);
+ }
+ @Override
+ public PackageStats onNewPackageStats(Parcel in) {
+ return new PkgUsageStatsExtended(in);
+ }
+ @Override
+ public ConfigurationStats onNewConfigurationStats(Configuration config) {
+ return new ConfigUsageStatsExtended(config);
+ }
+ @Override
+ public ConfigurationStats onNewConfigurationStats(Parcel source) {
+ return new ConfigUsageStatsExtended(source);
+ }
+ }
+
static class TimeStats {
int mCount;
final int[] mTimes = new int[NUM_LAUNCH_TIME_BINS];
@@ -166,27 +190,18 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
}
- static class PkgUsageStatsExtended {
+ static class PkgUsageStatsExtended extends UsageStats.PackageStats {
final ArrayMap<String, TimeStats> mLaunchTimes
= new ArrayMap<String, TimeStats>();
final ArrayMap<String, TimeStats> mFullyDrawnTimes
= new ArrayMap<String, TimeStats>();
- int mLaunchCount;
- long mUsageTime;
- long mPausedTime;
- long mResumedTime;
- PkgUsageStatsExtended() {
- mLaunchCount = 0;
- mUsageTime = 0;
+ PkgUsageStatsExtended(String pkgName) {
+ super(pkgName);
}
PkgUsageStatsExtended(Parcel in) {
- mLaunchCount = in.readInt();
- mUsageTime = in.readLong();
- if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount
- + ", Usage time:" + mUsageTime);
-
+ super(in);
final int numLaunchTimeStats = in.readInt();
if (localLOGV) Slog.v(TAG, "Reading launch times: " + numLaunchTimeStats);
mLaunchTimes.ensureCapacity(numLaunchTimeStats);
@@ -208,18 +223,6 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
}
- void updateResume(String comp, boolean launched) {
- if (launched) {
- mLaunchCount++;
- }
- mResumedTime = SystemClock.elapsedRealtime();
- }
-
- void updatePause() {
- mPausedTime = SystemClock.elapsedRealtime();
- mUsageTime += (mPausedTime - mResumedTime);
- }
-
void addLaunchCount(String comp) {
TimeStats times = mLaunchTimes.get(comp);
if (times == null) {
@@ -247,9 +250,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
times.add(millis);
}
- void writeToParcel(Parcel out) {
- out.writeInt(mLaunchCount);
- out.writeLong(mUsageTime);
+ public void writeExtendedToParcel(Parcel out, int parcelableFlags) {
final int numLaunchTimeStats = mLaunchTimes.size();
out.writeInt(numLaunchTimeStats);
for (int i=0; i<numLaunchTimeStats; i++) {
@@ -264,11 +265,21 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
}
- void clear() {
+ @Override
+ public boolean clearUsageTimes() {
mLaunchTimes.clear();
mFullyDrawnTimes.clear();
- mLaunchCount = 0;
- mUsageTime = 0;
+ return super.clearUsageTimes();
+ }
+ }
+
+ static class ConfigUsageStatsExtended extends UsageStats.ConfigurationStats {
+ ConfigUsageStatsExtended(Configuration config) {
+ super(config);
+ }
+
+ ConfigUsageStatsExtended(Parcel in) {
+ super(in);
}
}
@@ -364,18 +375,9 @@ public final class UsageStatsService extends IUsageStats.Stub {
+ VERSION + "; dropping");
return;
}
- int N = in.readInt();
- while (N > 0) {
- N--;
- String pkgName = in.readString();
- if (pkgName == null) {
- break;
- }
- if (localLOGV) Slog.v(TAG, "Reading package #" + N + ": " + pkgName);
- PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
- synchronized (mStatsLock) {
- mStats.put(pkgName, pus);
- }
+ LocalUsageStats stats = new LocalUsageStats(in, true);
+ synchronized (mStatsLock) {
+ mStats = stats;
}
}
@@ -419,12 +421,9 @@ public final class UsageStatsService extends IUsageStats.Stub {
try {
long lastResumeTime = Long.parseLong(lastResumeTimeStr);
synchronized (mStatsLock) {
- ArrayMap<String, Long> lrt = mLastResumeTimes.get(pkg);
- if (lrt == null) {
- lrt = new ArrayMap<String, Long>();
- mLastResumeTimes.put(pkg, lrt);
- }
- lrt.put(comp, lastResumeTime);
+ PkgUsageStatsExtended pus = (PkgUsageStatsExtended)
+ mStats.getOrCreatePackageStats(pkg);
+ pus.componentResumeTimes.put(comp, lastResumeTime);
}
} catch (NumberFormatException e) {
}
@@ -543,6 +542,15 @@ public final class UsageStatsService extends IUsageStats.Stub {
return;
}
+ Parcel out = Parcel.obtain();
+ synchronized (mStatsLock) {
+ out.writeInt(VERSION);
+ mStats.writeExtendedToParcel(out, 0);
+ if (dayChanged) {
+ mStats.clearUsageTimes();
+ }
+ }
+
synchronized (mFileLock) {
// Get the most recent file
mFileLeaf = getCurrentDateStr(FILE_PREFIX);
@@ -553,6 +561,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
if (!backupFile.exists()) {
if (!mFile.renameTo(backupFile)) {
Slog.w(TAG, "Failed to persist new stats");
+ out.recycle();
return;
}
} else {
@@ -562,14 +571,10 @@ public final class UsageStatsService extends IUsageStats.Stub {
try {
// Write mStats to file
- writeStatsFLOCK(mFile);
+ writeStatsFLOCK(mFile, out);
mLastWriteElapsedTime.set(currElapsedTime);
if (dayChanged) {
mLastWriteDay.set(curDay);
- // clear stats
- synchronized (mStats) {
- mStats.clear();
- }
mFile = new File(mDir, mFileLeaf);
checkFileLimitFLOCK();
}
@@ -590,17 +595,15 @@ public final class UsageStatsService extends IUsageStats.Stub {
backupFile.renameTo(mFile);
}
}
+ out.recycle();
}
if (localLOGV) Slog.d(TAG, "Dumped usage stats.");
}
- private void writeStatsFLOCK(File file) throws IOException {
+ private void writeStatsFLOCK(File file, Parcel parcel) throws IOException {
FileOutputStream stream = new FileOutputStream(file);
try {
- Parcel out = Parcel.obtain();
- writeStatsToParcelFLOCK(out);
- stream.write(out.marshall());
- out.recycle();
+ stream.write(parcel.marshall());
stream.flush();
} finally {
FileUtils.sync(stream);
@@ -608,29 +611,14 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
}
- private void writeStatsToParcelFLOCK(Parcel out) {
- synchronized (mStatsLock) {
- out.writeInt(VERSION);
- Set<String> keys = mStats.keySet();
- out.writeInt(keys.size());
- for (String key : keys) {
- PkgUsageStatsExtended pus = mStats.get(key);
- out.writeString(key);
- pus.writeToParcel(out);
- }
- }
- }
-
/** Filter out stats for any packages which aren't present anymore. */
private void filterHistoryStats() {
synchronized (mStatsLock) {
IPackageManager pm = AppGlobals.getPackageManager();
- for (int i=0; i<mLastResumeTimes.size(); i++) {
- String pkg = mLastResumeTimes.keyAt(i);
+ for (int i=mStats.mPackages.size()-1; i>=0; i--) {
try {
- if (pm.getPackageUid(pkg, 0) < 0) {
- mLastResumeTimes.removeAt(i);
- i--;
+ if (pm.getPackageUid(mStats.mPackages.valueAt(i).getPackageName(), 0) < 0) {
+ mStats.mPackages.removeAt(i);
}
} catch (RemoteException e) {
}
@@ -648,10 +636,12 @@ public final class UsageStatsService extends IUsageStats.Stub {
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
out.startTag(null, "usage-history");
synchronized (mStatsLock) {
- for (int i=0; i<mLastResumeTimes.size(); i++) {
+ int NP = mStats.mPackages.size();
+ for (int i=0; i<NP; i++) {
+ UsageStats.PackageStats ps = mStats.mPackages.valueAt(i);
out.startTag(null, "pkg");
- out.attribute(null, "name", mLastResumeTimes.keyAt(i));
- ArrayMap<String, Long> comp = mLastResumeTimes.valueAt(i);
+ out.attribute(null, "name", ps.getPackageName());
+ ArrayMap<String, Long> comp = ps.componentResumeTimes;
for (int j=0; j<comp.size(); j++) {
out.startTag(null, "comp");
out.attribute(null, "name", comp.keyAt(j));
@@ -678,6 +668,10 @@ public final class UsageStatsService extends IUsageStats.Stub {
ServiceManager.addService(SERVICE_NAME, asBinder());
}
+ public void systemReady() {
+ mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
+ }
+
/**
* Start watching packages to remove stats when a package is uninstalled.
* May only be called when the package manager is ready.
@@ -687,7 +681,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
@Override
public void onPackageRemovedAllUsers(String packageName, int uid) {
synchronized (mStatsLock) {
- mLastResumeTimes.remove(packageName);
+ mStats.mPackages.remove(packageName);
}
}
};
@@ -729,9 +723,10 @@ public final class UsageStatsService extends IUsageStats.Stub {
// to recover.
if (REPORT_UNEXPECTED) Slog.i(TAG, "Unexpected resume of " + pkgName
+ " while already resumed in " + mLastResumedPkg);
- PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg);
+ PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(
+ mLastResumedPkg);
if (pus != null) {
- pus.updatePause();
+ pus.pause();
}
}
}
@@ -744,22 +739,13 @@ public final class UsageStatsService extends IUsageStats.Stub {
mLastResumedComp = componentName.getClassName();
if (localLOGV) Slog.i(TAG, "started component:" + pkgName);
- PkgUsageStatsExtended pus = mStats.get(pkgName);
- if (pus == null) {
- pus = new PkgUsageStatsExtended();
- mStats.put(pkgName, pus);
- }
- pus.updateResume(mLastResumedComp, !samePackage);
+ PkgUsageStatsExtended pus = (PkgUsageStatsExtended)
+ mStats.getOrCreatePackageStats(pkgName);
+ pus.resume(!samePackage);
if (!sameComp) {
pus.addLaunchCount(mLastResumedComp);
}
-
- ArrayMap<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName);
- if (componentResumeTimes == null) {
- componentResumeTimes = new ArrayMap<String, Long>();
- mLastResumeTimes.put(pkgName, componentResumeTimes);
- }
- componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis());
+ pus.componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis());
}
}
@@ -782,13 +768,13 @@ public final class UsageStatsService extends IUsageStats.Stub {
if (localLOGV) Slog.i(TAG, "paused component:"+pkgName);
- PkgUsageStatsExtended pus = mStats.get(pkgName);
+ PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName);
if (pus == null) {
// Weird some error here
Slog.i(TAG, "No package stats for pkg:"+pkgName);
return;
}
- pus.updatePause();
+ pus.pause();
}
// Persist current data to file if needed.
@@ -808,7 +794,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
writeStatsToFile(false, false);
synchronized (mStatsLock) {
- PkgUsageStatsExtended pus = mStats.get(pkgName);
+ PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName);
if (pus != null) {
pus.addLaunchTime(componentName.getClassName(), millis);
}
@@ -827,13 +813,29 @@ public final class UsageStatsService extends IUsageStats.Stub {
writeStatsToFile(false, false);
synchronized (mStatsLock) {
- PkgUsageStatsExtended pus = mStats.get(pkgName);
+ PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName);
if (pus != null) {
pus.addFullyDrawnTime(componentName.getClassName(), millis);
}
}
}
+ public void noteStartConfig(Configuration config) {
+ enforceCallingPermission();
+ synchronized (mStatsLock) {
+ config = new Configuration(config);
+ ConfigUsageStatsExtended cus = (ConfigUsageStatsExtended)
+ mStats.getOrCreateConfigurationStats(config);
+ if (cus != mCurrentConfigStats) {
+ if (mCurrentConfigStats != null) {
+ mCurrentConfigStats.stop();
+ }
+ cus.start();
+ mCurrentConfigStats = cus;
+ }
+ }
+ }
+
public void enforceCallingPermission() {
if (Binder.getCallingPid() == Process.myPid()) {
return;
@@ -843,53 +845,71 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
@Override
- public PkgUsageStats getPkgUsageStats(ComponentName componentName) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_USAGE_STATS, null);
+ public UsageStats.PackageStats getPkgUsageStats(String callingPkg,
+ ComponentName componentName) {
+ checkCallerPermission(callingPkg, "getPkgUsageStats");
String pkgName;
if ((componentName == null) ||
((pkgName = componentName.getPackageName()) == null)) {
return null;
}
synchronized (mStatsLock) {
- PkgUsageStatsExtended pus = mStats.get(pkgName);
- Map<String, Long> lastResumeTimes = mLastResumeTimes.get(pkgName);
- if (pus == null && lastResumeTimes == null) {
+ PkgUsageStatsExtended pus = (PkgUsageStatsExtended)mStats.getPackageStats(pkgName);
+ if (pus == null) {
return null;
}
- int launchCount = pus != null ? pus.mLaunchCount : 0;
- long usageTime = pus != null ? pus.mUsageTime : 0;
- return new PkgUsageStats(pkgName, launchCount, usageTime, lastResumeTimes);
+ return new UsageStats.PackageStats(pus);
}
}
@Override
- public PkgUsageStats[] getAllPkgUsageStats() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.PACKAGE_USAGE_STATS, null);
+ public UsageStats.PackageStats[] getAllPkgUsageStats(String callingPkg) {
+ checkCallerPermission(callingPkg, "getAllPkgUsageStats");
synchronized (mStatsLock) {
- int size = mLastResumeTimes.size();
- if (size <= 0) {
+ int NP = mStats.mPackages.size();
+ if (NP <= 0) {
return null;
}
- PkgUsageStats retArr[] = new PkgUsageStats[size];
- for (int i=0; i<size; i++) {
- String pkg = mLastResumeTimes.keyAt(i);
- long usageTime = 0;
- int launchCount = 0;
-
- PkgUsageStatsExtended pus = mStats.get(pkg);
- if (pus != null) {
- usageTime = pus.mUsageTime;
- launchCount = pus.mLaunchCount;
- }
- retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime,
- mLastResumeTimes.valueAt(i));
+ UsageStats.PackageStats retArr[] = new UsageStats.PackageStats[NP];
+ for (int p=0; p<NP; p++) {
+ UsageStats.PackageStats ps = mStats.mPackages.valueAt(p);
+ retArr[p] = new UsageStats.PackageStats(ps);
}
return retArr;
}
}
+ @Override
+ public ParcelableParcel getCurrentStats(String callingPkg) {
+ checkCallerPermission(callingPkg, "getCurrentStats");
+ synchronized (mStatsLock) {
+ ParcelableParcel out = new ParcelableParcel(null);
+ mStats.writeToParcel(out.getParcel(), 0);
+ return out;
+ }
+ }
+
+ private void checkCallerPermission(String callingPkg, String callingOp) {
+ // Because the permission for this is system-only, its use with
+ // app ops is a little different: the op is disabled by default,
+ // and enabling it allows apps to get access even if they don't
+ // hold the permission.
+ int mode = mAppOps.noteOpNoThrow(AppOpsManager.OP_GET_USAGE_STATS, Binder.getCallingUid(),
+ callingPkg);
+ if (mode == AppOpsManager.MODE_ALLOWED) {
+ return;
+ } else if (mode != AppOpsManager.MODE_IGNORED) {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.PACKAGE_USAGE_STATS)
+ == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ }
+
+ String msg = "Package " + callingPkg + " not allowed to call " + callingOp;
+ throw new SecurityException(msg);
+ }
+
static byte[] readFully(FileInputStream stream) throws IOException {
int pos = 0;
int avail = stream.available();
@@ -963,31 +983,28 @@ public final class UsageStatsService extends IUsageStats.Stub {
return;
}
- pw.println(sb.toString());
- int N = in.readInt();
+ final LocalUsageStats stats = new LocalUsageStats(in, true);
+ final long time = SystemClock.elapsedRealtime();
- while (N > 0) {
- N--;
- String pkgName = in.readString();
- if (pkgName == null) {
- break;
- }
+ pw.println(sb.toString());
+ int NP = stats.mPackages.size();
+ for (int p=0; p<NP; p++) {
+ PkgUsageStatsExtended pus = (PkgUsageStatsExtended)stats.mPackages.valueAt(p);
sb.setLength(0);
- PkgUsageStatsExtended pus = new PkgUsageStatsExtended(in);
- if (packages != null && !packages.contains(pkgName)) {
+ if (packages != null && !packages.contains(pus.getPackageName())) {
// This package has not been requested -- don't print
// anything for it.
} else if (isCompactOutput) {
sb.append("P:");
- sb.append(pkgName);
+ sb.append(pus.getPackageName());
sb.append(',');
- sb.append(pus.mLaunchCount);
+ sb.append(pus.getLaunchCount());
sb.append(',');
- sb.append(pus.mUsageTime);
+ sb.append(pus.getUsageTime(time));
sb.append('\n');
final int NLT = pus.mLaunchTimes.size();
for (int i=0; i<NLT; i++) {
- sb.append("A:");
+ sb.append("L:");
String activity = pus.mLaunchTimes.keyAt(i);
sb.append(activity);
TimeStats times = pus.mLaunchTimes.valueAt(i);
@@ -1001,7 +1018,7 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
final int NFDT = pus.mFullyDrawnTimes.size();
for (int i=0; i<NFDT; i++) {
- sb.append("A:");
+ sb.append("D:");
String activity = pus.mFullyDrawnTimes.keyAt(i);
sb.append(activity);
TimeStats times = pus.mFullyDrawnTimes.valueAt(i);
@@ -1011,15 +1028,23 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
sb.append('\n');
}
+ final int NC = pus.componentResumeTimes.size();
+ for (int c=0; c<NC; c++) {
+ pw.print("R:"); pw.print(pus.componentResumeTimes.keyAt(c)); pw.print(",");
+ pw.println(pus.componentResumeTimes.valueAt(c));
+ }
} else {
sb.append(" ");
- sb.append(pkgName);
- sb.append(": ");
- sb.append(pus.mLaunchCount);
- sb.append(" times, ");
- sb.append(pus.mUsageTime);
- sb.append(" ms");
+ sb.append(pus.getPackageName());
+ if (pus.getLaunchCount() != 0 || pus.getUsageTime(time) != 0) {
+ sb.append(": ");
+ sb.append(pus.getLaunchCount());
+ sb.append(" times, ");
+ TimeUtils.formatDuration(pus.getUsageTime(time), sb);
+ } else {
+ sb.append(":");
+ }
sb.append('\n');
final int NLT = pus.mLaunchTimes.size();
for (int i=0; i<NLT; i++) {
@@ -1084,10 +1109,50 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
sb.append('\n');
}
+ final int NC = pus.componentResumeTimes.size();
+ for (int c=0; c<NC; c++) {
+ sb.append(" ");
+ sb.append(pus.componentResumeTimes.keyAt(c));
+ sb.append(" last resumed ");
+ sb.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+ pus.componentResumeTimes.valueAt(c)).toString());
+ sb.append('\n');
+ }
}
pw.write(sb.toString());
}
+ if (packages == null) {
+ int NC = stats.mConfigurations.size();
+ for (int c=0; c<NC; c++) {
+ ConfigUsageStatsExtended cus
+ = (ConfigUsageStatsExtended)stats.mConfigurations.valueAt(c);
+ sb.setLength(0);
+ if (isCompactOutput) {
+ sb.append("C:"); sb.append(cus.getConfiguration().toString());
+ sb.append(","); sb.append(cus.getUsageCount()); sb.append(",");
+ sb.append(cus.getUsageTime(time));
+ } else {
+ sb.append(" ");
+ sb.append(cus.getConfiguration().toString());
+ sb.append(":\n");
+ if (cus.getUsageCount() != 0 || cus.getUsageTime(time) != 0) {
+ sb.append(" Used ");
+ sb.append(cus.getUsageCount());
+ sb.append(" times, ");
+ TimeUtils.formatDuration(cus.getUsageTime(time), sb);
+ sb.append("\n");
+ }
+ if (cus.getLastUsedTime() > 0) {
+ sb.append(" Last used: ");
+ sb.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+ cus.getLastUsedTime()).toString());
+ sb.append("\n");
+ }
+ }
+ pw.write(sb.toString());
+ }
+ }
}
/**
@@ -1174,5 +1239,4 @@ public final class UsageStatsService extends IUsageStats.Stub {
collectDumpInfoFLOCK(pw, isCompactOutput, deleteAfterPrint, packages);
}
}
-
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 3ae0fd5..3d5fb57 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -518,6 +518,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Brighten quickly.
slow = false;
}
+ // If low power mode is enabled, brightness level
+ // would be scaled down to half
+ if (mPowerRequest.lowPowerMode) {
+ target = target/2;
+ }
animateScreenBrightness(clampScreenBrightness(target),
slow ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
} else {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 54cb035..0f5805c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -323,6 +323,10 @@ public class InputManagerService extends IInputManager.Stub
mHandler.sendEmptyMessage(MSG_RELOAD_DEVICE_ALIASES);
mHandler.sendEmptyMessage(MSG_UPDATE_KEYBOARD_LAYOUTS);
+
+ if (mWiredAccessoryCallbacks != null) {
+ mWiredAccessoryCallbacks.systemReady();
+ }
}
private void reloadKeyboardLayouts() {
@@ -1588,6 +1592,7 @@ public class InputManagerService extends IInputManager.Stub
*/
public interface WiredAccessoryCallbacks {
public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask);
+ public void systemReady();
}
/**
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
new file mode 100644
index 0000000..c8b1ba0
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import java.util.Comparator;
+
+/**
+ * Sorts notificaitons into attention-relelvant order.
+ */
+public class NotificationComparator
+ implements Comparator<NotificationManagerService.NotificationRecord> {
+
+ @Override
+ public int compare(NotificationManagerService.NotificationRecord lhs,
+ NotificationManagerService.NotificationRecord rhs) {
+ final int leftScore = lhs.sbn.getScore();
+ final int rightScore = rhs.sbn.getScore();
+ if (leftScore != rightScore) {
+ // by priority, high to low
+ return -1 * Integer.compare(leftScore, rightScore);
+ }
+ final float leftPeple = lhs.getContactAffinity();
+ final float rightPeople = rhs.getContactAffinity();
+ if (leftPeple != rightPeople) {
+ // by contact proximity, close to far
+ return -1 * Float.compare(leftPeple, rightPeople);
+ }
+ // then break ties by time, most recent first
+ return -1 * Long.compare(lhs.sbn.getPostTime(), rhs.sbn.getPostTime());
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fce86e8..7a4f951 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -49,8 +49,10 @@ import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IInterface;
+import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
@@ -61,6 +63,7 @@ import android.service.notification.INotificationListener;
import android.service.notification.IConditionListener;
import android.service.notification.IConditionProvider;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationOrderUpdate;
import android.service.notification.StatusBarNotification;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
@@ -76,7 +79,6 @@ import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
import com.android.internal.R;
-import com.android.internal.notification.NotificationScorer;
import com.android.internal.util.FastXmlSerializer;
import com.android.server.EventLogTags;
import com.android.server.SystemService;
@@ -104,9 +106,12 @@ import java.lang.reflect.Array;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
/** {@hide} */
public class NotificationManagerService extends SystemService {
@@ -118,6 +123,8 @@ public class NotificationManagerService extends SystemService {
// message codes
static final int MESSAGE_TIMEOUT = 2;
static final int MESSAGE_SAVE_POLICY_FILE = 3;
+ static final int MESSAGE_RECONSIDER_RANKING = 4;
+ static final int MESSAGE_SEND_RANKING_UPDATE = 5;
static final int LONG_DELAY = 3500; // 3.5 seconds
static final int SHORT_DELAY = 2000; // 2 seconds
@@ -147,6 +154,9 @@ public class NotificationManagerService extends SystemService {
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
+ private final HandlerThread mRankingThread = new HandlerThread("ranker",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ private Handler mRankingHandler = null;
private Light mNotificationLight;
Light mAttentionLight;
@@ -171,6 +181,7 @@ public class NotificationManagerService extends SystemService {
// used as a mutex for access to all active notifications & listeners
final ArrayList<NotificationRecord> mNotificationList =
new ArrayList<NotificationRecord>();
+ final NotificationComparator mRankingComparator = new NotificationComparator();
final ArrayMap<String, NotificationRecord> mNotificationsByKey =
new ArrayMap<String, NotificationRecord>();
final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
@@ -193,7 +204,7 @@ public class NotificationManagerService extends SystemService {
private static final String TAG_PACKAGE = "package";
private static final String ATTR_NAME = "name";
- final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
+ final ArrayList<NotificationSignalExtractor> mSignalExtractors = new ArrayList<NotificationSignalExtractor>();
private final UserProfiles mUserProfiles = new UserProfiles();
private NotificationListeners mListeners;
@@ -444,8 +455,9 @@ public class NotificationManagerService extends SystemService {
public static final class NotificationRecord
{
final StatusBarNotification sbn;
- final SingleNotificationStats stats = new SingleNotificationStats();
+ SingleNotificationStats stats;
IBinder statusBarKey;
+ private float mContactAffinity;
NotificationRecord(StatusBarNotification sbn)
{
@@ -528,6 +540,14 @@ public class NotificationManagerService extends SystemService {
this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
this.sbn.getNotification());
}
+
+ public void setContactAffinity(float contactAffinity) {
+ mContactAffinity = contactAffinity;
+ }
+
+ public float getContactAffinity() {
+ return mContactAffinity;
+ }
}
private static final class ToastRecord
@@ -707,7 +727,7 @@ public class NotificationManagerService extends SystemService {
boolean queryRemove = false;
boolean packageChanged = false;
boolean cancelNotifications = true;
-
+
if (action.equals(Intent.ACTION_PACKAGE_ADDED)
|| (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)
@@ -849,6 +869,8 @@ public class NotificationManagerService extends SystemService {
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
mHandler = new WorkerHandler();
+ mRankingThread.start();
+ mRankingHandler = new RankingWorkerHandler(mRankingThread.getLooper());
mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@Override
@@ -925,21 +947,22 @@ public class NotificationManagerService extends SystemService {
mSettingsObserver = new SettingsObserver(mHandler);
- // spin up NotificationScorers
- String[] notificationScorerNames = resources.getStringArray(
- R.array.config_notificationScorers);
- for (String scorerName : notificationScorerNames) {
+ // spin up NotificationSignalExtractors
+ String[] extractorNames = resources.getStringArray(
+ R.array.config_notificationSignalExtractors);
+ for (String extractorName : extractorNames) {
try {
- Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName);
- NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance();
- scorer.initialize(getContext());
- mScorers.add(scorer);
+ Class<?> extractorClass = getContext().getClassLoader().loadClass(extractorName);
+ NotificationSignalExtractor extractor =
+ (NotificationSignalExtractor) extractorClass.newInstance();
+ extractor.initialize(getContext());
+ mSignalExtractors.add(extractor);
} catch (ClassNotFoundException e) {
- Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e);
+ Slog.w(TAG, "Couldn't find extractor " + extractorName + ".", e);
} catch (InstantiationException e) {
- Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e);
+ Slog.w(TAG, "Couldn't instantiate extractor " + extractorName + ".", e);
} catch (IllegalAccessException e) {
- Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e);
+ Slog.w(TAG, "Problem accessing extractor " + extractorName + ".", e);
}
}
@@ -1150,6 +1173,7 @@ public class NotificationManagerService extends SystemService {
* System-only API for getting a list of current (i.e. not cleared) notifications.
*
* Requires ACCESS_NOTIFICATIONS which is signature|system.
+ * @returns A list of all the notifications, in natural order.
*/
@Override
public StatusBarNotification[] getActiveNotifications(String callingPkg) {
@@ -1306,6 +1330,9 @@ public class NotificationManagerService extends SystemService {
* should be used.
*
* @param token The binder for the listener, to check that the caller is allowed
+ * @param keys the notification keys to fetch, or null for all active notifications.
+ * @returns The return value will contain the notifications specified in keys, in that
+ * order, or if keys is null, all the notifications, in natural order.
*/
@Override
public StatusBarNotification[] getActiveNotificationsFromListener(
@@ -1337,7 +1364,7 @@ public class NotificationManagerService extends SystemService {
@Override
public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
- return NotificationManagerService.this.getActiveNotificationKeysFromListener(token);
+ return NotificationManagerService.this.getActiveNotificationKeys(token);
}
@Override
@@ -1409,19 +1436,21 @@ public class NotificationManagerService extends SystemService {
}
};
- private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
- synchronized (mNotificationList) {
- final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- final ArrayList<String> keys = new ArrayList<String>();
- final int N = mNotificationList.size();
- for (int i=0; i<N; i++) {
- final StatusBarNotification sbn = mNotificationList.get(i).sbn;
- if (info.enabledAndUserMatches(sbn.getUserId())) {
- keys.add(sbn.getKey());
+ private String[] getActiveNotificationKeys(INotificationListener token) {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ final ArrayList<String> keys = new ArrayList<String>();
+ if (info.isEnabledForCurrentProfiles()) {
+ synchronized (mNotificationList) {
+ final int N = mNotificationList.size();
+ for (int i = 0; i < N; i++) {
+ final StatusBarNotification sbn = mNotificationList.get(i).sbn;
+ if (info.enabledAndUserMatches(sbn.getUserId())) {
+ keys.add(sbn.getKey());
+ }
}
}
- return keys.toArray(new String[keys.size()]);
}
+ return keys.toArray(new String[keys.size()]);
}
void dumpImpl(PrintWriter pw) {
@@ -1578,26 +1607,23 @@ public class NotificationManagerService extends SystemService {
// 1. initial score: buckets of 10, around the app
int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
- // 2. Consult external heuristics (TBD)
-
- // 3. Apply local rules
-
- int initialScore = score;
- if (!mScorers.isEmpty()) {
- if (DBG) Slog.v(TAG, "Initial score is " + score + ".");
- for (NotificationScorer scorer : mScorers) {
+ // 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);
+ if (!mSignalExtractors.isEmpty()) {
+ for (NotificationSignalExtractor extractor : mSignalExtractors) {
try {
- score = scorer.getScore(notification, score);
+ RankingFuture future = extractor.process(r);
+ scheduleRankingReconsideration(future);
} catch (Throwable t) {
- Slog.w(TAG, "Scorer threw on .getScore.", t);
+ Slog.w(TAG, "NotificationSignalExtractor failed.", t);
}
}
- if (DBG) Slog.v(TAG, "Final score is " + score + ".");
}
- // add extra to indicate score modified by NotificationScorer
- notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED,
- score != initialScore);
+ // 3. Apply local rules
// blocked apps
if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
@@ -1608,10 +1634,6 @@ public class NotificationManagerService extends SystemService {
}
}
- if (DBG) {
- Slog.v(TAG, "Assigned score=" + score + " to " + notification);
- }
-
if (score < SCORE_DISPLAY_THRESHOLD) {
// Notification will be blocked because the score is too low.
return;
@@ -1626,12 +1648,7 @@ public class NotificationManagerService extends SystemService {
if (DBG || intercept) Slog.v(TAG,
"pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept);
synchronized (mNotificationList) {
- final StatusBarNotification n = new StatusBarNotification(
- pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
- user);
- NotificationRecord r = new NotificationRecord(n);
NotificationRecord old = null;
-
int index = indexOfNotificationLocked(pkg, tag, id, userId);
if (index < 0) {
mNotificationList.add(r);
@@ -1639,7 +1656,7 @@ public class NotificationManagerService extends SystemService {
} else {
old = mNotificationList.get(index);
mNotificationList.set(index, r);
- mUsageStats.registerUpdatedByApp(r);
+ mUsageStats.registerUpdatedByApp(r, old);
// Make sure we don't lose the foreground service state.
if (old != null) {
notification.flags |=
@@ -1651,6 +1668,8 @@ public class NotificationManagerService extends SystemService {
}
mNotificationsByKey.put(n.getKey(), r);
+ Collections.sort(mNotificationList, mRankingComparator);
+
// Ensure if this is a foreground service that the proper additional
// flags are set.
if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
@@ -1948,6 +1967,57 @@ public class NotificationManagerService extends SystemService {
}
}
+ private void scheduleRankingReconsideration(RankingFuture future) {
+ if (future != null) {
+ Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, future);
+ long delay = future.getDelay(TimeUnit.MILLISECONDS);
+ mRankingHandler.sendMessageDelayed(m, delay);
+ }
+ }
+
+ private void handleRankingReconsideration(Message message) {
+ if (!(message.obj instanceof RankingFuture)) return;
+
+ RankingFuture future = (RankingFuture) message.obj;
+ future.run();
+ try {
+ NotificationRecord record = future.get();
+ synchronized (mNotificationList) {
+ int before = mNotificationList.indexOf(record);
+ if (before != -1) {
+ Collections.sort(mNotificationList, mRankingComparator);
+ int after = mNotificationList.indexOf(record);
+
+ if (before != after) {
+ scheduleSendRankingUpdate();
+ }
+ }
+ }
+ } catch (InterruptedException e) {
+ // we're running the future explicitly, so this should never happen
+ } catch (ExecutionException e) {
+ // we're running the future explicitly, so this should never happen
+ }
+ }
+
+ private void scheduleSendRankingUpdate() {
+ mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
+ Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
+ mHandler.sendMessage(m);
+ }
+
+ private void handleSendRankingUpdate() {
+ synchronized (mNotificationList) {
+ final int N = mNotificationList.size();
+ ArrayList<StatusBarNotification> sbns =
+ new ArrayList<StatusBarNotification>(N);
+ for (int i = 0; i < N; i++ ) {
+ sbns.add(mNotificationList.get(i).sbn);
+ }
+ mListeners.notifyOrderUpdateLocked(sbns);
+ }
+ }
+
private final class WorkerHandler extends Handler
{
@Override
@@ -1961,10 +2031,29 @@ public class NotificationManagerService extends SystemService {
case MESSAGE_SAVE_POLICY_FILE:
handleSavePolicyFile();
break;
+ case MESSAGE_SEND_RANKING_UPDATE:
+ handleSendRankingUpdate();
+ break;
}
}
+
}
+ private final class RankingWorkerHandler extends Handler
+ {
+ public RankingWorkerHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_RECONSIDER_RANKING:
+ handleRankingReconsideration(msg);
+ break;
+ }
+ }
+ }
// Notifications
// ============================================================================
@@ -2346,9 +2435,9 @@ public class NotificationManagerService extends SystemService {
@Override
public void onServiceAdded(ManagedServiceInfo info) {
final INotificationListener listener = (INotificationListener) info.service;
- final String[] keys = getActiveNotificationKeysFromListener(listener);
+ final String[] keys = getActiveNotificationKeys(listener);
try {
- listener.onListenerConnected(keys);
+ listener.onListenerConnected(new NotificationOrderUpdate(keys));
} catch (RemoteException e) {
// we tried
}
@@ -2361,12 +2450,18 @@ public class NotificationManagerService extends SystemService {
// make a copy in case changes are made to the underlying Notification object
final StatusBarNotification sbnClone = sbn.clone();
for (final ManagedServiceInfo info : mServices) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- notifyPostedIfUserMatch(info, sbnClone);
+ if (info.isEnabledForCurrentProfiles()) {
+ final INotificationListener listener = (INotificationListener) info.service;
+ final String[] keys = getActiveNotificationKeys(listener);
+ if (keys.length > 0) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyPostedIfUserMatch(info, sbnClone, keys);
+ }
+ });
}
- });
+ }
}
}
@@ -2378,39 +2473,83 @@ public class NotificationManagerService extends SystemService {
// NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
// notification
final StatusBarNotification sbnLight = sbn.cloneLight();
- for (ManagedServiceInfo serviceInfo : mServices) {
- final ManagedServiceInfo info = (ManagedServiceInfo) serviceInfo;
+ for (final ManagedServiceInfo info : mServices) {
+ if (info.isEnabledForCurrentProfiles()) {
+ final INotificationListener listener = (INotificationListener) info.service;
+ final String[] keys = getActiveNotificationKeys(listener);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ notifyRemovedIfUserMatch(info, sbnLight, keys);
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * asynchronously notify all listeners about a reordering of notifications
+ * @param sbns an array of {@link StatusBarNotification}s to consider. This code
+ * must not rely on mutable members of these objects, such as the
+ * {@link Notification}.
+ */
+ public void notifyOrderUpdateLocked(final ArrayList<StatusBarNotification> sbns) {
+ for (final ManagedServiceInfo serviceInfo : mServices) {
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyRemovedIfUserMatch(info, sbnLight);
+ notifyOrderUpdateIfUserMatch(serviceInfo, sbns);
}
});
}
}
- private void notifyPostedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) {
+ private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
+ final StatusBarNotification sbn, String[] keys) {
if (!info.enabledAndUserMatches(sbn.getUserId())) {
return;
}
final INotificationListener listener = (INotificationListener)info.service;
try {
- listener.onNotificationPosted(sbn);
+ listener.onNotificationPosted(sbn, new NotificationOrderUpdate(keys));
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
}
}
- private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) {
+ private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn,
+ String[] keys) {
if (!info.enabledAndUserMatches(sbn.getUserId())) {
return;
}
final INotificationListener listener = (INotificationListener)info.service;
try {
- listener.onNotificationRemoved(sbn);
+ listener.onNotificationRemoved(sbn, new NotificationOrderUpdate(keys));
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
}
}
+
+ /**
+ * @param sbns an array of {@link StatusBarNotification}s to consider. This code
+ * must not rely on mutable members of these objects, such as the
+ * {@link Notification}.
+ */
+ public void notifyOrderUpdateIfUserMatch(ManagedServiceInfo info,
+ ArrayList<StatusBarNotification> sbns) {
+ ArrayList<String> keys = new ArrayList<String>(sbns.size());
+ for (StatusBarNotification sbn: sbns) {
+ if (info.enabledAndUserMatches(sbn.getUserId())) {
+ keys.add(sbn.getKey());
+ }
+ }
+ final INotificationListener listener = (INotificationListener)info.service;
+ try {
+ listener.onNotificationOrderUpdate(
+ new NotificationOrderUpdate(keys.toArray(new String[keys.size()])));
+ } catch (RemoteException ex) {
+ Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
new file mode 100644
index 0000000..a41fdfe
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
@@ -0,0 +1,41 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES 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.content.Context;
+
+/**
+ * Extracts signals that will be useful to the {@link NotificationComparator} and caches them
+ * on the {@link NotificationManagerService.NotificationRecord} object. These annotations will
+ * not be passed on to {@link android.service.notification.NotificationListenerService}s.
+ */
+public interface NotificationSignalExtractor {
+
+ /** One-time initialization. */
+ public void initialize(Context context);
+
+ /**
+ * Called once per notification that is posted or updated.
+ *
+ * @return null if the work is done, or a future if there is more to do. The
+ * {@link RankingFuture} will be run on a worker thread, and if notifications are re-ordered
+ * by that execution, the {@link NotificationManagerService} may send order update
+ * events to the {@link android.service.notification.NotificationListenerService}s.
+ */
+ public RankingFuture process(NotificationManagerService.NotificationRecord notification);
+
+}
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index 45ab3d3..a60e95b 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -58,6 +58,7 @@ public class NotificationUsageStats {
* Called when a notification has been posted.
*/
public synchronized void registerPostedByApp(NotificationRecord notification) {
+ notification.stats = new SingleNotificationStats();
notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime();
for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
stats.numPostedByApp++;
@@ -68,7 +69,8 @@ public class NotificationUsageStats {
/**
* Called when a notification has been updated.
*/
- public void registerUpdatedByApp(NotificationRecord notification) {
+ public void registerUpdatedByApp(NotificationRecord notification, NotificationRecord old) {
+ notification.stats = old.stats;
for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
stats.numUpdatedByApp++;
}
diff --git a/services/core/java/com/android/server/notification/RankingFuture.java b/services/core/java/com/android/server/notification/RankingFuture.java
new file mode 100644
index 0000000..33aad8d
--- /dev/null
+++ b/services/core/java/com/android/server/notification/RankingFuture.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 java.util.concurrent.Delayed;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public abstract class RankingFuture
+ implements ScheduledFuture<NotificationManagerService.NotificationRecord> {
+ private static final long IMMEDIATE = 0l;
+
+ private static final int START = 0;
+ private static final int RUNNING = 1;
+ private static final int DONE = 2;
+ private static final int CANCELLED = 3;
+
+ private int mState;
+ private long mDelay;
+ protected NotificationManagerService.NotificationRecord mRecord;
+
+ public RankingFuture(NotificationManagerService.NotificationRecord record) {
+ this(record, IMMEDIATE);
+ }
+
+ public RankingFuture(NotificationManagerService.NotificationRecord record, long delay) {
+ mDelay = delay;
+ mRecord = record;
+ mState = START;
+ }
+
+ public void run() {
+ if (mState == START) {
+ mState = RUNNING;
+
+ work();
+
+ mState = DONE;
+ synchronized (this) {
+ notifyAll();
+ }
+ }
+ }
+
+ @Override
+ public long getDelay(TimeUnit unit) {
+ return unit.convert(mDelay, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public int compareTo(Delayed another) {
+ return Long.compare(getDelay(TimeUnit.MICROSECONDS),
+ another.getDelay(TimeUnit.MICROSECONDS));
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ if (mState == START) { // can't cancel if running or done
+ mState = CANCELLED;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return mState == CANCELLED;
+ }
+
+ @Override
+ public boolean isDone() {
+ return mState == DONE;
+ }
+
+ @Override
+ public NotificationManagerService.NotificationRecord get()
+ throws InterruptedException, ExecutionException {
+ while (!isDone()) {
+ synchronized (this) {
+ this.wait();
+ }
+ }
+ return mRecord;
+ }
+
+ @Override
+ public NotificationManagerService.NotificationRecord get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ long timeoutMillis = unit.convert(timeout, TimeUnit.MILLISECONDS);
+ long start = System.currentTimeMillis();
+ long now = System.currentTimeMillis();
+ while (!isDone() && (now - start) < timeoutMillis) {
+ try {
+ wait(timeoutMillis - (now - start));
+ } catch (InterruptedException e) {
+ now = System.currentTimeMillis();
+ }
+ }
+ return mRecord;
+ }
+
+ public abstract void work();
+}
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
new file mode 100644
index 0000000..8cd2f9b2
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -0,0 +1,298 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES 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.Notification;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.LruCache;
+import android.util.Slog;
+
+import com.android.server.notification.NotificationManagerService.NotificationRecord;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+/**
+ * This {@link NotificationSignalExtractor} attempts to validate
+ * people references. Also elevates the priority of real people.
+ */
+public class ValidateNotificationPeople implements NotificationSignalExtractor {
+ private static final String TAG = "ValidateNotificationPeople";
+ private static final boolean INFO = true;
+ private static final boolean DEBUG = false;
+
+ private static final boolean ENABLE_PEOPLE_VALIDATOR = true;
+ private static final String SETTING_ENABLE_PEOPLE_VALIDATOR =
+ "validate_notification_people_enabled";
+ private static final String[] LOOKUP_PROJECTION = { Contacts._ID };
+ private static final int MAX_PEOPLE = 10;
+ private static final int PEOPLE_CACHE_SIZE = 200;
+
+ private static final float NONE = 0f;
+ private static final float VALID_CONTACT = 0.5f;
+ // TODO private static final float STARRED_CONTACT = 1f;
+
+ protected boolean mEnabled;
+ private Context mContext;
+
+ // maps raw person handle to resolved person object
+ private LruCache<String, LookupResult> mPeopleCache;
+
+ private RankingFuture validatePeople(NotificationRecord record) {
+ float affinity = NONE;
+ Bundle extras = record.getNotification().extras;
+ if (extras == null) {
+ return null;
+ }
+
+ final String[] people = getExtraPeople(extras);
+ if (people == null || people.length == 0) {
+ return null;
+ }
+
+ if (INFO) Slog.i(TAG, "Validating: " + record.sbn.getKey());
+ final LinkedList<String> pendingLookups = new LinkedList<String>();
+ for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
+ final String handle = people[personIdx];
+ if (TextUtils.isEmpty(handle)) continue;
+
+ synchronized (mPeopleCache) {
+ LookupResult lookupResult = mPeopleCache.get(handle);
+ if (lookupResult == null || lookupResult.isExpired()) {
+ pendingLookups.add(handle);
+ } else {
+ if (DEBUG) Slog.d(TAG, "using cached lookupResult: " + lookupResult.mId);
+ }
+ if (lookupResult != null) {
+ affinity = Math.max(affinity, lookupResult.getAffinity());
+ }
+ }
+ }
+
+ // record the best available data, so far:
+ record.setContactAffinity(affinity);
+
+ if (pendingLookups.isEmpty()) {
+ if (INFO) Slog.i(TAG, "final affinity: " + affinity);
+ return null;
+ }
+
+ if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + record.sbn.getKey());
+ return new RankingFuture(record) {
+ @Override
+ public void work() {
+ if (INFO) Slog.i(TAG, "Executing: validation for: " + mRecord.sbn.getKey());
+ float affinity = NONE;
+ LookupResult lookupResult = null;
+ for (final String handle: pendingLookups) {
+ final Uri uri = Uri.parse(handle);
+ if ("tel".equals(uri.getScheme())) {
+ if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
+ lookupResult = resolvePhoneContact(handle, uri.getSchemeSpecificPart());
+ } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
+ if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
+ lookupResult = resolveContactsUri(handle, uri);
+ } else {
+ Slog.w(TAG, "unsupported URI " + handle);
+ }
+ }
+ if (lookupResult != null) {
+ affinity = Math.max(affinity, lookupResult.getAffinity());
+ }
+
+ float affinityBound = mRecord.getContactAffinity();
+ affinity = Math.max(affinity, affinityBound);
+ mRecord.setContactAffinity(affinity);
+ if (INFO) Slog.i(TAG, "final affinity: " + affinity);
+ }
+ };
+ }
+
+ private String[] getExtraPeople(Bundle extras) {
+ String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE);
+ if (people != null) {
+ return people;
+ }
+
+ ArrayList<String> stringArray = extras.getStringArrayList(Notification.EXTRA_PEOPLE);
+ if (stringArray != null) {
+ return (String[]) stringArray.toArray();
+ }
+
+ String string = extras.getString(Notification.EXTRA_PEOPLE);
+ if (string != null) {
+ people = new String[1];
+ people[0] = string;
+ return people;
+ }
+ char[] charArray = extras.getCharArray(Notification.EXTRA_PEOPLE);
+ if (charArray != null) {
+ people = new String[1];
+ people[0] = new String(charArray);
+ return people;
+ }
+
+ CharSequence charSeq = extras.getCharSequence(Notification.EXTRA_PEOPLE);
+ if (charSeq != null) {
+ people = new String[1];
+ people[0] = charSeq.toString();
+ return people;
+ }
+
+ CharSequence[] charSeqArray = extras.getCharSequenceArray(Notification.EXTRA_PEOPLE);
+ if (charSeqArray != null) {
+ final int N = charSeqArray.length;
+ people = new String[N];
+ for (int i = 0; i < N; i++) {
+ people[i] = charSeqArray[i].toString();
+ }
+ return people;
+ }
+
+ ArrayList<CharSequence> charSeqList =
+ extras.getCharSequenceArrayList(Notification.EXTRA_PEOPLE);
+ if (charSeqList != null) {
+ final int N = charSeqList.size();
+ people = new String[N];
+ for (int i = 0; i < N; i++) {
+ people[i] = charSeqList.get(i).toString();
+ }
+ return people;
+ }
+ return null;
+ }
+
+ private LookupResult resolvePhoneContact(final String handle, final String number) {
+ LookupResult lookupResult = null;
+ Cursor c = null;
+ try {
+ Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
+ Uri.encode(number));
+ c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null);
+ if (c != null && c.getCount() > 0) {
+ c.moveToFirst();
+ final int idIdx = c.getColumnIndex(Contacts._ID);
+ final int id = c.getInt(idIdx);
+ if (DEBUG) Slog.d(TAG, "is valid: " + id);
+ lookupResult = new LookupResult(id);
+ }
+ } catch(Throwable t) {
+ Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ if (lookupResult == null) {
+ lookupResult = new LookupResult(LookupResult.INVALID_ID);
+ }
+ synchronized (mPeopleCache) {
+ mPeopleCache.put(handle, lookupResult);
+ }
+ return lookupResult;
+ }
+
+ private LookupResult resolveContactsUri(String handle, final Uri personUri) {
+ LookupResult lookupResult = null;
+ Cursor c = null;
+ try {
+ c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null);
+ if (c != null && c.getCount() > 0) {
+ c.moveToFirst();
+ final int idIdx = c.getColumnIndex(Contacts._ID);
+ final int id = c.getInt(idIdx);
+ if (DEBUG) Slog.d(TAG, "is valid: " + id);
+ lookupResult = new LookupResult(id);
+ }
+ } catch(Throwable t) {
+ Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ if (lookupResult == null) {
+ lookupResult = new LookupResult(LookupResult.INVALID_ID);
+ }
+ synchronized (mPeopleCache) {
+ mPeopleCache.put(handle, lookupResult);
+ }
+ return lookupResult;
+ }
+
+ public void initialize(Context context) {
+ if (DEBUG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + ".");
+ mContext = context;
+ mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
+ mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt(
+ mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
+ }
+
+ public RankingFuture process(NotificationManagerService.NotificationRecord record) {
+ if (!mEnabled) {
+ if (INFO) Slog.i(TAG, "disabled");
+ return null;
+ }
+ if (record == null || record.getNotification() == null) {
+ if (INFO) Slog.i(TAG, "skipping empty notification");
+ return null;
+ }
+ return validatePeople(record);
+ }
+
+ private static class LookupResult {
+ private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000; // 1hr
+ public static final int INVALID_ID = -1;
+
+ private final long mExpireMillis;
+ private int mId;
+
+ public LookupResult(int id) {
+ mId = id;
+ mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
+ }
+
+ public boolean isExpired() {
+ return mExpireMillis < System.currentTimeMillis();
+ }
+
+ public boolean isInvalid() {
+ return mId == INVALID_ID || isExpired();
+ }
+
+ public float getAffinity() {
+ if (isInvalid()) {
+ return NONE;
+ } else {
+ return VALID_CONTACT; // TODO: finer grained result: stars
+ }
+ }
+
+ public LookupResult setId(int id) {
+ mId = id;
+ return this;
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c8b61f1..a7f4b28 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -613,6 +613,12 @@ public class PackageManagerService extends IPackageManager.Stub {
private final AtomicLong mLastWritten = new AtomicLong(0);
private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false);
+ private boolean mIsFirstBoot = false;
+
+ boolean isFirstBoot() {
+ return mIsFirstBoot;
+ }
+
void write(boolean force) {
if (force) {
write();
@@ -701,6 +707,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.mLastPackageUsageTimeInMills = timeInMillis;
}
} catch (FileNotFoundException expected) {
+ mIsFirstBoot = true;
} catch (IOException e) {
Log.w(TAG, "Failed to read package usage times", e);
} finally {
@@ -1745,7 +1752,7 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public boolean isFirstBoot() {
- return !mRestoredSettings;
+ return !mRestoredSettings || mPackageUsage.isFirstBoot();
}
@Override
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 60c6313..60212bf 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -288,6 +288,20 @@ public class UserManagerService extends IUserManager.Stub {
return users;
}
+ @Override
+ public UserInfo getProfileParent(int userHandle) {
+ checkManageUsersPermission("get the profile parent");
+ synchronized (mPackagesLock) {
+ UserInfo profile = getUserInfoLocked(userHandle);
+ int parentUserId = profile.profileGroupId;
+ if (parentUserId == UserInfo.NO_PROFILE_GROUP_ID) {
+ return null;
+ } else {
+ return getUserInfoLocked(parentUserId);
+ }
+ }
+ }
+
private boolean isProfileOf(UserInfo user, UserInfo profile) {
return user.id == profile.id ||
(user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
@@ -1022,17 +1036,6 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- private int getNextProfileGroupIdLocked() {
- int maxGroupId = UserInfo.NO_PROFILE_GROUP_ID;
- for (int i = 0; i < mUsers.size(); i++) {
- UserInfo ui = mUsers.valueAt(i);
- if (maxGroupId < ui.profileGroupId) {
- maxGroupId = ui.profileGroupId;
- }
- }
- return maxGroupId + 1;
- }
-
@Override
public UserInfo createProfileForUser(String name, int flags, int userId) {
checkManageUsersPermission("Only the system can create users");
@@ -1049,16 +1052,16 @@ public class UserManagerService extends IUserManager.Stub {
return createUserInternal(name, flags, UserHandle.USER_NULL);
}
- private UserInfo createUserInternal(String name, int flags, int profileId) {
+ private UserInfo createUserInternal(String name, int flags, int parentId) {
final long ident = Binder.clearCallingIdentity();
UserInfo userInfo = null;
try {
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
- UserInfo profile = null;
- if (profileId != UserHandle.USER_NULL) {
- profile = getUserInfoLocked(profileId);
- if (profile == null) return null;
+ UserInfo parent = null;
+ if (parentId != UserHandle.USER_NULL) {
+ parent = getUserInfoLocked(parentId);
+ if (parent == null) return null;
}
if (isUserLimitReachedLocked()) return null;
int userId = getNextAvailableIdLocked();
@@ -1071,12 +1074,12 @@ public class UserManagerService extends IUserManager.Stub {
Environment.getUserSystemDirectory(userInfo.id).mkdirs();
mUsers.put(userId, userInfo);
writeUserListLocked();
- if (profile != null) {
- if (profile.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
- profile.profileGroupId = getNextProfileGroupIdLocked();
- writeUserLocked(profile);
+ if (parent != null) {
+ if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
+ parent.profileGroupId = parent.id;
+ writeUserLocked(parent);
}
- userInfo.profileGroupId = profile.profileGroupId;
+ userInfo.profileGroupId = parent.profileGroupId;
}
writeUserLocked(userInfo);
mPm.createNewUserLILPw(userId, userPath);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 6d2e859..47a8b2e 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1634,6 +1634,8 @@ public final class PowerManagerService extends com.android.server.SystemService
mDisplayPowerRequest.blockScreenOn = mScreenOnBlocker.isHeld();
+ mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled;
+
mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
mRequestWaitForNegativeProximity);
mRequestWaitForNegativeProximity = false;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 9039236..99ec242 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -116,7 +116,8 @@ public class AppTransition implements Dump {
/** Fraction of animation at which the recents thumbnail becomes completely transparent */
private static final float RECENTS_THUMBNAIL_FADEOUT_FRACTION = 0.25f;
- private static final long DEFAULT_APP_TRANSITION_DURATION = 250;
+ private static final int DEFAULT_APP_TRANSITION_DURATION = 250;
+ private static final int THUMBNAIL_APP_TRANSITION_DURATION = 225;
private final Context mContext;
private final Handler mH;
@@ -160,6 +161,7 @@ public class AppTransition implements Dump {
private final int mConfigShortAnimTime;
private final Interpolator mDecelerateInterpolator;
private final Interpolator mThumbnailFadeoutInterpolator;
+ private final Interpolator mThumbnailCubicInterpolator;
private int mCurrentUserId = 0;
@@ -170,6 +172,8 @@ public class AppTransition implements Dump {
com.android.internal.R.integer.config_shortAnimTime);
mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.decelerate_cubic);
+ mThumbnailCubicInterpolator = AnimationUtils.loadInterpolator(context,
+ com.android.internal.R.interpolator.fast_out_slow_in);
mThumbnailFadeoutInterpolator = new Interpolator() {
@Override
public float getInterpolation(float input) {
@@ -401,11 +405,23 @@ public class AppTransition implements Dump {
/**
* Prepares the specified animation with a standard duration, interpolator, etc.
*/
+ Animation prepareThumbnailAnimationWithDuration(Animation a, int appWidth, int appHeight,
+ int duration, Interpolator interpolator) {
+ a.setDuration(duration);
+ a.setFillAfter(true);
+ a.setInterpolator(interpolator);
+ a.initialize(appWidth, appHeight, appWidth, appHeight);
+ return a;
+ }
+
+ /**
+ * Prepares the specified animation with a standard duration, interpolator, etc.
+ */
Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
// Pick the desired duration. If this is an inter-activity transition,
// it is the standard duration for that. Otherwise we use the longer
// task transition duration.
- final long duration;
+ final int duration;
switch (transit) {
case TRANSIT_ACTIVITY_OPEN:
case TRANSIT_ACTIVITY_CLOSE:
@@ -415,11 +431,8 @@ public class AppTransition implements Dump {
duration = DEFAULT_APP_TRANSITION_DURATION;
break;
}
- a.setDuration(duration);
- a.setFillAfter(true);
- a.setInterpolator(mDecelerateInterpolator);
- a.initialize(appWidth, appHeight, appWidth, appHeight);
- return a;
+ return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight, duration,
+ mDecelerateInterpolator);
}
/**
@@ -594,7 +607,8 @@ public class AppTransition implements Dump {
throw new RuntimeException("Invalid thumbnail transition state");
}
- return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+ return prepareThumbnailAnimationWithDuration(a, appWidth, appHeight,
+ THUMBNAIL_APP_TRANSITION_DURATION, mThumbnailCubicInterpolator);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 637beec..836a19c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5038,6 +5038,10 @@ public class WindowManagerService extends IWindowManager.Stub
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
+ if (token == null) {
+ throw new IllegalArgumentException("token == null");
+ }
+
mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
KeyguardDisableHandler.KEYGUARD_DISABLE, new Pair<IBinder, String>(token, tag)));
}
@@ -5049,6 +5053,10 @@ public class WindowManagerService extends IWindowManager.Stub
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
+ if (token == null) {
+ throw new IllegalArgumentException("token == null");
+ }
+
mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
KeyguardDisableHandler.KEYGUARD_REENABLE, token));
}
@@ -5062,6 +5070,11 @@ public class WindowManagerService extends IWindowManager.Stub
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
}
+
+ if (callback == null) {
+ throw new IllegalArgumentException("callback == null");
+ }
+
mPolicy.exitKeyguardSecurely(new WindowManagerPolicy.OnKeyguardExitResult() {
@Override
public void onKeyguardExitResult(boolean success) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9a9f1c8..1980d1e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -97,6 +97,7 @@ import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -113,6 +114,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final String DEVICE_POLICIES_XML = "device_policies.xml";
+ private static final String LOCK_TASK_COMPONENTS_XML = "lock-task-component";
+
private static final int REQUEST_EXPIRE_PASSWORD = 5571;
private static final long MS_PER_DAY = 86400 * 1000;
@@ -127,6 +130,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final boolean DBG = false;
final Context mContext;
+ final UserManager mUserManager;
final PowerManager.WakeLock mWakeLock;
IPowerManager mIPowerManager;
@@ -182,6 +186,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final ArrayList<ActiveAdmin> mAdminList
= new ArrayList<ActiveAdmin>();
+ // This is the list of component allowed to start lock task mode.
+ final List<ComponentName> mLockTaskComponents = new ArrayList<ComponentName>();
+
public DevicePolicyData(int userHandle) {
mUserHandle = userHandle;
}
@@ -203,7 +210,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
+ action + " for user " + userHandle);
mHandler.post(new Runnable() {
public void run() {
- handlePasswordExpirationNotification(getUserData(userHandle));
+ handlePasswordExpirationNotification(userHandle);
}
});
}
@@ -605,6 +612,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
*/
public DevicePolicyManagerService(Context context) {
mContext = context;
+ mUserManager = UserManager.get(mContext);
mHasFeature = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_DEVICE_ADMIN);
mWakeLock = ((PowerManager)context.getSystemService(Context.POWER_SERVICE))
@@ -812,6 +820,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
sendAdminCommandLocked(admin, action, null);
}
+ /**
+ * Send an update to one specific admin, get notified when that admin returns a result.
+ */
void sendAdminCommandLocked(ActiveAdmin admin, String action, BroadcastReceiver result) {
Intent intent = new Intent(action);
intent.setComponent(admin.info.getComponent());
@@ -826,12 +837,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ /**
+ * Send an update to all admins of a user that enforce a specified policy.
+ */
void sendAdminCommandLocked(String action, int reqPolicy, int userHandle) {
final DevicePolicyData policy = getUserData(userHandle);
final int count = policy.mAdminList.size();
if (count > 0) {
for (int i = 0; i < count; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
+ final ActiveAdmin admin = policy.mAdminList.get(i);
if (admin.info.usesPolicy(reqPolicy)) {
sendAdminCommandLocked(admin, action);
}
@@ -839,6 +853,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ /**
+ * Send an update intent to all admins of a user and its profiles. Only send to admins that
+ * enforce a specified policy.
+ */
+ private void sendAdminCommandToSelfAndProfilesLocked(String action, int reqPolicy,
+ int userHandle) {
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo ui : profiles) {
+ int id = ui.getUserHandle().getIdentifier();
+ sendAdminCommandLocked(action, reqPolicy, id);
+ }
+ }
+
void removeActiveAdminLocked(final ComponentName adminReceiver, int userHandle) {
final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (admin != null) {
@@ -955,6 +982,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
out.endTag(null, "active-password");
}
+ for (int i=0; i<policy.mLockTaskComponents.size(); i++) {
+ ComponentName component = policy.mLockTaskComponents.get(i);
+ out.startTag(null, LOCK_TASK_COMPONENTS_XML);
+ out.attribute(null, "name", component.flattenToString());
+ out.endTag(null, LOCK_TASK_COMPONENTS_XML);
+ }
+
out.endTag(null, "policies");
out.endDocument();
@@ -1004,6 +1038,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
type = parser.next();
int outerDepth = parser.getDepth();
+ policy.mLockTaskComponents.clear();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -1056,6 +1091,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
policy.mActivePasswordNonLetter = Integer.parseInt(
parser.getAttributeValue(null, "nonletter"));
XmlUtils.skipCurrentTag(parser);
+ } else if (LOCK_TASK_COMPONENTS_XML.equals(tag)) {
+ policy.mLockTaskComponents.add
+ (ComponentName.unflattenFromString
+ (parser.getAttributeValue(null, "name")));
+ XmlUtils.skipCurrentTag(parser);
} else {
Slog.w(LOG_TAG, "Unknown tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -1171,23 +1211,29 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- private void handlePasswordExpirationNotification(DevicePolicyData policy) {
+ private void handlePasswordExpirationNotification(int userHandle) {
synchronized (this) {
final long now = System.currentTimeMillis();
- final int N = policy.mAdminList.size();
- if (N <= 0) {
- return;
- }
- for (int i=0; i < N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)
- && admin.passwordExpirationTimeout > 0L
- && admin.passwordExpirationDate > 0L
- && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS) {
- sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING);
+
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo ui : profiles) {
+ int profileUserHandle = ui.getUserHandle().getIdentifier();
+ final DevicePolicyData policy = getUserData(profileUserHandle);
+ final int count = policy.mAdminList.size();
+ if (count > 0) {
+ for (int i = 0; i < count; i++) {
+ final ActiveAdmin admin = policy.mAdminList.get(i);
+ if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)
+ && admin.passwordExpirationTimeout > 0L
+ && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS
+ && admin.passwordExpirationDate > 0L) {
+ sendAdminCommandLocked(admin,
+ DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING);
+ }
+ }
}
}
- setExpirationAlarmCheckLocked(mContext, policy);
+ setExpirationAlarmCheckLocked(mContext, getUserData(userHandle));
}
}
@@ -1197,8 +1243,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
if (! hasCert) {
if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
- UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- for (UserInfo user : um.getUsers()) {
+ for (UserInfo user : mUserManager.getUsers()) {
notificationManager.cancelAsUser(
null, MONITORING_CERT_NOTIFICATION_ID, user.getUserHandle());
}
@@ -1237,8 +1282,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// If this is a boot intent, this will fire for each user. But if this is a storage changed
// intent, it will fire once, so we need to notify all users.
if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
- UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- for (UserInfo user : um.getUsers()) {
+ for (UserInfo user : mUserManager.getUsers()) {
notificationManager.notifyAsUser(
null, MONITORING_CERT_NOTIFICATION_ID, noti, user.getUserHandle());
}
@@ -1415,18 +1459,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
enforceCrossUserPermission(userHandle);
synchronized (this) {
int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
- DevicePolicyData policy = getUserData(userHandle);
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
return admin != null ? admin.passwordQuality : mode;
}
- final int N = policy.mAdminList.size();
- for (int i=0; i<N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (mode < admin.passwordQuality) {
- mode = admin.passwordQuality;
+ // Return strictest policy for this user and profiles that are visible from this user.
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (mode < admin.passwordQuality) {
+ mode = admin.passwordQuality;
+ }
}
}
return mode;
@@ -1457,7 +1505,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
enforceCrossUserPermission(userHandle);
synchronized (this) {
- DevicePolicyData policy = getUserData(userHandle);
int length = 0;
if (who != null) {
@@ -1465,11 +1512,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return admin != null ? admin.minimumPasswordLength : length;
}
- final int N = policy.mAdminList.size();
- for (int i=0; i<N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (length < admin.minimumPasswordLength) {
- length = admin.minimumPasswordLength;
+ // Return strictest policy for this user and profiles that are visible from this user.
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordLength) {
+ length = admin.minimumPasswordLength;
+ }
}
}
return length;
@@ -1500,7 +1552,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
enforceCrossUserPermission(userHandle);
synchronized (this) {
- DevicePolicyData policy = getUserData(userHandle);
int length = 0;
if (who != null) {
@@ -1508,11 +1559,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return admin != null ? admin.passwordHistoryLength : length;
}
- final int N = policy.mAdminList.size();
- for (int i = 0; i < N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (length < admin.passwordHistoryLength) {
- length = admin.passwordHistoryLength;
+ // Return strictest policy for this user and profiles that are visible from this user.
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.passwordHistoryLength) {
+ length = admin.passwordHistoryLength;
+ }
}
}
return length;
@@ -1558,19 +1614,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
enforceCrossUserPermission(userHandle);
synchronized (this) {
+ long timeout = 0L;
+
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
- return admin != null ? admin.passwordExpirationTimeout : 0L;
+ return admin != null ? admin.passwordExpirationTimeout : timeout;
}
- long timeout = 0L;
- DevicePolicyData policy = getUserData(userHandle);
- final int N = policy.mAdminList.size();
- for (int i = 0; i < N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (timeout == 0L || (admin.passwordExpirationTimeout != 0L
- && timeout > admin.passwordExpirationTimeout)) {
- timeout = admin.passwordExpirationTimeout;
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (timeout == 0L || (admin.passwordExpirationTimeout != 0L
+ && timeout > admin.passwordExpirationTimeout)) {
+ timeout = admin.passwordExpirationTimeout;
+ }
}
}
return timeout;
@@ -1582,19 +1642,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
* Returns 0 if not configured.
*/
private long getPasswordExpirationLocked(ComponentName who, int userHandle) {
+ long timeout = 0L;
+
if (who != null) {
ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
- return admin != null ? admin.passwordExpirationDate : 0L;
+ return admin != null ? admin.passwordExpirationDate : timeout;
}
- long timeout = 0L;
- DevicePolicyData policy = getUserData(userHandle);
- final int N = policy.mAdminList.size();
- for (int i = 0; i < N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (timeout == 0L || (admin.passwordExpirationDate != 0
- && timeout > admin.passwordExpirationDate)) {
- timeout = admin.passwordExpirationDate;
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (timeout == 0L || (admin.passwordExpirationDate != 0
+ && timeout > admin.passwordExpirationDate)) {
+ timeout = admin.passwordExpirationDate;
+ }
}
}
return timeout;
@@ -1641,12 +1705,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return admin != null ? admin.minimumPasswordUpperCase : length;
}
- DevicePolicyData policy = getUserData(userHandle);
- final int N = policy.mAdminList.size();
- for (int i=0; i<N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (length < admin.minimumPasswordUpperCase) {
- length = admin.minimumPasswordUpperCase;
+ // Return strictest policy for this user and profiles that are visible from this user.
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordUpperCase) {
+ length = admin.minimumPasswordUpperCase;
+ }
}
}
return length;
@@ -1681,12 +1749,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return admin != null ? admin.minimumPasswordLowerCase : length;
}
- DevicePolicyData policy = getUserData(userHandle);
- final int N = policy.mAdminList.size();
- for (int i=0; i<N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (length < admin.minimumPasswordLowerCase) {
- length = admin.minimumPasswordLowerCase;
+ // Return strictest policy for this user and profiles that are visible from this user.
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordLowerCase) {
+ length = admin.minimumPasswordLowerCase;
+ }
}
}
return length;
@@ -1724,12 +1796,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return admin != null ? admin.minimumPasswordLetters : length;
}
- DevicePolicyData policy = getUserData(userHandle);
- final int N = policy.mAdminList.size();
- for (int i=0; i<N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (length < admin.minimumPasswordLetters) {
- length = admin.minimumPasswordLetters;
+ // Return strictest policy for this user and profiles that are visible from this user.
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordLetters) {
+ length = admin.minimumPasswordLetters;
+ }
}
}
return length;
@@ -1767,12 +1843,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return admin != null ? admin.minimumPasswordNumeric : length;
}
- DevicePolicyData policy = getUserData(userHandle);
- final int N = policy.mAdminList.size();
- for (int i = 0; i < N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (length < admin.minimumPasswordNumeric) {
- length = admin.minimumPasswordNumeric;
+ // Return strictest policy for this user and profiles that are visible from this user.
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordNumeric) {
+ length = admin.minimumPasswordNumeric;
+ }
}
}
return length;
@@ -1810,12 +1890,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return admin != null ? admin.minimumPasswordSymbols : length;
}
- DevicePolicyData policy = getUserData(userHandle);
- final int N = policy.mAdminList.size();
- for (int i=0; i<N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (length < admin.minimumPasswordSymbols) {
- length = admin.minimumPasswordSymbols;
+ // Return strictest policy for this user and profiles that are visible from this user.
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordSymbols) {
+ length = admin.minimumPasswordSymbols;
+ }
}
}
return length;
@@ -1853,12 +1937,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return admin != null ? admin.minimumPasswordNonLetter : length;
}
- DevicePolicyData policy = getUserData(userHandle);
- final int N = policy.mAdminList.size();
- for (int i=0; i<N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (length < admin.minimumPasswordNonLetter) {
- length = admin.minimumPasswordNonLetter;
+ // Return strictest policy for this user and profiles that are visible from this user.
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (length < admin.minimumPasswordNonLetter) {
+ length = admin.minimumPasswordNonLetter;
+ }
}
}
return length;
@@ -1870,8 +1958,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return true;
}
enforceCrossUserPermission(userHandle);
+
synchronized (this) {
- DevicePolicyData policy = getUserData(userHandle);
+
+ // The active password is stored in the user that runs the launcher
+ // If the user this is called from is part of a profile group, that is the parent
+ // of the group.
+ UserInfo parent = getProfileParent(userHandle);
+ int id = parent == null ? userHandle : parent.id;
+ DevicePolicyData policy = getUserData(id);
+
// This API can only be called by an active device admin,
// so try to retrieve it to check that the caller is one.
getActiveAdminForCallerLocked(null,
@@ -1893,13 +1989,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
public int getCurrentFailedPasswordAttempts(int userHandle) {
- enforceCrossUserPermission(userHandle);
synchronized (this) {
// This API can only be called by an active device admin,
// so try to retrieve it to check that the caller is one.
getActiveAdminForCallerLocked(null,
DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
- return getUserData(userHandle).mFailedPasswordAttempts;
+
+ // The active password is stored in the parent.
+ DevicePolicyData policy = getUserData(getProfileParent(userHandle).id);
+
+ return policy.mFailedPasswordAttempts;
}
}
@@ -1909,6 +2008,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
enforceCrossUserPermission(userHandle);
synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
// This API can only be called by an active device admin,
// so try to retrieve it to check that the caller is one.
getActiveAdminForCallerLocked(who,
@@ -1928,7 +2030,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
enforceCrossUserPermission(userHandle);
synchronized (this) {
- DevicePolicyData policy = getUserData(userHandle);
int count = 0;
if (who != null) {
@@ -1936,14 +2037,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return admin != null ? admin.maximumFailedPasswordsForWipe : count;
}
- final int N = policy.mAdminList.size();
- for (int i=0; i<N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (count == 0) {
- count = admin.maximumFailedPasswordsForWipe;
- } else if (admin.maximumFailedPasswordsForWipe != 0
- && count > admin.maximumFailedPasswordsForWipe) {
- count = admin.maximumFailedPasswordsForWipe;
+ // Return strictest policy for this user and profiles that are visible from this user.
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (count == 0) {
+ count = admin.maximumFailedPasswordsForWipe;
+ } else if (admin.maximumFailedPasswordsForWipe != 0
+ && count > admin.maximumFailedPasswordsForWipe) {
+ count = admin.maximumFailedPasswordsForWipe;
+ }
}
}
return count;
@@ -1955,9 +2061,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
enforceCrossUserPermission(userHandle);
+ enforceNotManagedProfile(userHandle, "reset the password");
+
int quality;
synchronized (this) {
- // This API can only be called by an active device admin,
+ // This api can only be called by an active device admin,
// so try to retrieve it to check that the caller is one.
getActiveAdminForCallerLocked(null,
DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
@@ -2135,15 +2243,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return admin != null ? admin.maximumTimeToUnlock : time;
}
- DevicePolicyData policy = getUserData(userHandle);
- final int N = policy.mAdminList.size();
- for (int i=0; i<N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (time == 0) {
- time = admin.maximumTimeToUnlock;
- } else if (admin.maximumTimeToUnlock != 0
- && time > admin.maximumTimeToUnlock) {
- time = admin.maximumTimeToUnlock;
+ // Return strictest policy for this user and profiles that are visible from this user.
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+ final int N = policy.mAdminList.size();
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (time == 0) {
+ time = admin.maximumTimeToUnlock;
+ } else if (admin.maximumTimeToUnlock != 0
+ && time > admin.maximumTimeToUnlock) {
+ time = admin.maximumTimeToUnlock;
+ }
}
}
return time;
@@ -2301,7 +2413,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
public void run() {
try {
ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER);
- ((UserManager) mContext.getSystemService(Context.USER_SERVICE))
+ (mUserManager)
.removeUser(userHandle);
} catch (RemoteException re) {
// Shouldn't happen
@@ -2349,6 +2461,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return;
}
enforceCrossUserPermission(userHandle);
+ enforceNotManagedProfile(userHandle, "set the active password");
+
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
DevicePolicyData p = getUserData(userHandle);
@@ -2377,7 +2491,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
saveSettingsLocked(userHandle);
updatePasswordExpirationsLocked(userHandle);
setExpirationAlarmCheckLocked(mContext, p);
- sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
+ sendAdminCommandToSelfAndProfilesLocked(
+ DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -2387,26 +2502,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
/**
- * Called any time the device password is updated. Resets all password expiration clocks.
+ * Called any time the device password is updated. Resets all password expiration clocks.
*/
private void updatePasswordExpirationsLocked(int userHandle) {
- DevicePolicyData policy = getUserData(userHandle);
- final int N = policy.mAdminList.size();
- if (N > 0) {
- for (int i=0; i<N; i++) {
- ActiveAdmin admin = policy.mAdminList.get(i);
- if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
- long timeout = admin.passwordExpirationTimeout;
- long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
- admin.passwordExpirationDate = expiration;
+ List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+ for (UserInfo userInfo : profiles) {
+ int profileId = userInfo.getUserHandle().getIdentifier();
+ DevicePolicyData policy = getUserData(profileId);
+ final int N = policy.mAdminList.size();
+ if (N > 0) {
+ for (int i=0; i<N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
+ long timeout = admin.passwordExpirationTimeout;
+ long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
+ admin.passwordExpirationDate = expiration;
+ }
+ }
}
+ saveSettingsLocked(profileId);
}
- saveSettingsLocked(userHandle);
- }
}
public void reportFailedPasswordAttempt(int userHandle) {
enforceCrossUserPermission(userHandle);
+ enforceNotManagedProfile(userHandle, "report failed password attempt");
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
@@ -2421,7 +2541,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (max > 0 && policy.mFailedPasswordAttempts >= max) {
wipeDeviceOrUserLocked(0, userHandle);
}
- sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_FAILED,
+ sendAdminCommandToSelfAndProfilesLocked(
+ DeviceAdminReceiver.ACTION_PASSWORD_FAILED,
DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
}
} finally {
@@ -2444,7 +2565,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
policy.mPasswordOwner = -1;
saveSettingsLocked(userHandle);
if (mHasFeature) {
- sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED,
+ sendAdminCommandToSelfAndProfilesLocked(
+ DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED,
DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
}
} finally {
@@ -2473,7 +2595,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Scan through active admins and find if anyone has already
// set the global proxy.
Set<ComponentName> compSet = policy.mAdminMap.keySet();
- for (ComponentName component : compSet) {
+ for (ComponentName component : compSet) {
ActiveAdmin ap = policy.mAdminMap.get(component);
if ((ap.specifiesGlobalProxy) && (!component.equals(who))) {
// Another admin already sets the global proxy
@@ -2502,8 +2624,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Reset the global proxy accordingly
// Do this using system permissions, as apps cannot write to secure settings
long origId = Binder.clearCallingIdentity();
- resetGlobalProxyLocked(policy);
- Binder.restoreCallingIdentity(origId);
+ try {
+ resetGlobalProxyLocked(policy);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
return null;
}
}
@@ -2888,8 +3013,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
- UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- if (um.getUserInfo(userHandle) == null) {
+ if (mUserManager.getUserInfo(userHandle) == null) {
// User doesn't exist.
throw new IllegalArgumentException(
"Attempted to set profile owner for invalid userId: " + userHandle);
@@ -2935,10 +3059,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
int userId = UserHandle.getCallingUserId();
Slog.d(LOG_TAG, "Enabling the profile for: " + userId);
- UserManager um = UserManager.get(mContext);
long id = Binder.clearCallingIdentity();
try {
- um.setUserEnabled(userId);
+ mUserManager.setUserEnabled(userId);
Intent intent = new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED);
intent.putExtra(Intent.EXTRA_USER, new UserHandle(UserHandle.getCallingUserId()));
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
@@ -3002,6 +3125,30 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ private void enforceNotManagedProfile(int userHandle, String message) {
+ if(isManagedProfile(userHandle)) {
+ throw new SecurityException("You can not " + message + " from a managed profile. ");
+ }
+ }
+
+ private UserInfo getProfileParent(int userHandle) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mUserManager.getProfileParent(userHandle);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private boolean isManagedProfile(int userHandle) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mUserManager.getUserInfo(userHandle).isManagedProfile();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
private void enableIfNecessary(String packageName, int userId) {
try {
IPackageManager ipm = AppGlobals.getPackageManager();
@@ -3105,10 +3252,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- UserManager um = UserManager.get(mContext);
long id = Binder.clearCallingIdentity();
try {
- um.setApplicationRestrictions(packageName, settings, userHandle);
+ mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
} finally {
restoreCallingIdentity(id);
}
@@ -3172,10 +3318,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- UserManager um = UserManager.get(mContext);
long id = Binder.clearCallingIdentity();
try {
- return um.getApplicationRestrictions(packageName, userHandle);
+ return mUserManager.getApplicationRestrictions(packageName, userHandle);
} finally {
restoreCallingIdentity(id);
}
@@ -3192,10 +3337,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- UserManager um = UserManager.get(mContext);
long id = Binder.clearCallingIdentity();
try {
- um.setUserRestriction(key, enabled, userHandle);
+ mUserManager.setUserRestriction(key, enabled, userHandle);
} finally {
restoreCallingIdentity(id);
}
@@ -3344,4 +3488,74 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return resultSet.toArray(new String[resultSet.size()]);
}
}
+
+ /**
+ * Sets which componets may enter lock task mode.
+ *
+ * This function can only be called by the device owner or the profile owner.
+ * @param components The list of components allowed to enter lock task mode.
+ */
+ public void setLockTaskComponents(ComponentName[] components) throws SecurityException {
+ // Get the package names of the caller.
+ int uid = Binder.getCallingUid();
+ String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
+
+ // Check whether any of the package name is the device owner or the profile owner.
+ for (int i=0; i<packageNames.length; i++) {
+ String packageName = packageNames[i];
+ int userHandle = UserHandle.getUserId(uid);
+ String profileOwnerPackage = getProfileOwner(userHandle);
+ if (isDeviceOwner(packageName) ||
+ (profileOwnerPackage != null && profileOwnerPackage.equals(packageName))) {
+
+ // If a package name is the device owner or the profile owner,
+ // we update the component list.
+ DevicePolicyData policy = getUserData(userHandle);
+ policy.mLockTaskComponents.clear();
+ if (components != null) {
+ for (int j=0; j<components.length; j++) {
+ ComponentName component = components[j];
+ policy.mLockTaskComponents.add(component);
+ }
+ }
+
+ // Store the settings persistently.
+ saveSettingsLocked(userHandle);
+ return;
+ }
+ }
+ throw new SecurityException();
+ }
+
+ /**
+ * This function returns the list of components allowed to start the task lock mode.
+ */
+ public ComponentName[] getLockTaskComponents() {
+ int userHandle = UserHandle.USER_OWNER;
+ DevicePolicyData policy = getUserData(userHandle);
+ ComponentName[] tempArray = policy.mLockTaskComponents.toArray(new ComponentName[0]);
+ return tempArray;
+ }
+
+ /**
+ * This function lets the caller know whether the given component is allowed to start the
+ * lock task mode.
+ * @param component The component to check
+ */
+ public boolean isLockTaskPermitted(ComponentName component) {
+ // Get current user's devicepolicy
+ int uid = Binder.getCallingUid();
+ int userHandle = UserHandle.getUserId(uid);
+ DevicePolicyData policy = getUserData(userHandle);
+ for (int i=0; i<policy.mLockTaskComponents.size(); i++) {
+ ComponentName lockTaskComponent = policy.mLockTaskComponents.get(i);
+
+ // If the given component equals one of the component stored our device-owner-set
+ // list, we allow this component to start the lock task mode.
+ if (lockTaskComponent.getPackageName().equals(component.getPackageName())) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 716823c..22e2a6e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -334,6 +334,7 @@ public final class SystemServer {
InputManagerService inputManager = null;
TelephonyRegistry telephonyRegistry = null;
ConsumerIrService consumerIr = null;
+ AudioService audioService = null;
boolean onlyCore = false;
boolean firstBoot = false;
@@ -769,7 +770,8 @@ public final class SystemServer {
if (!disableMedia && !"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
try {
Slog.i(TAG, "Audio Service");
- ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context));
+ audioService = new AudioService(context);
+ ServiceManager.addService(Context.AUDIO_SERVICE, audioService);
} catch (Throwable e) {
reportWtf("starting Audio Service", e);
}
@@ -1084,6 +1086,7 @@ public final class SystemServer {
final InputManagerService inputManagerF = inputManager;
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
final MediaRouterService mediaRouterF = mediaRouter;
+ final AudioService audioServiceF = audioService;
// We now tell the activity manager it is okay to run third party
// code. It will call back into us once it has gotten to the state
@@ -1152,6 +1155,11 @@ public final class SystemServer {
} catch (Throwable e) {
reportWtf("making Recognition Service ready", e);
}
+ try {
+ if (audioServiceF != null) audioServiceF.systemReady();
+ } catch (Throwable e) {
+ reportWtf("Notifying AudioService running", e);
+ }
Watchdog.getInstance().start();
// It is now okay to let the various system services start their
diff --git a/tests/Split/Android.mk b/tests/Split/Android.mk
new file mode 100644
index 0000000..7884d4d
--- /dev/null
+++ b/tests/Split/Android.mk
@@ -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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_PACKAGE_NAME := Split
+
+LOCAL_AAPT_FLAGS := --split fr,de
+LOCAL_AAPT_FLAGS += -v
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/Split/AndroidManifest.xml b/tests/Split/AndroidManifest.xml
new file mode 100644
index 0000000..a4956a7
--- /dev/null
+++ b/tests/Split/AndroidManifest.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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.example.split">
+ <application android:label="@string/app_title">
+ <activity android:name="ActivityMain">
+ <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/Split/assets/blah.txt b/tests/Split/assets/blah.txt
new file mode 100644
index 0000000..1b37e40
--- /dev/null
+++ b/tests/Split/assets/blah.txt
@@ -0,0 +1 @@
+This is some useful info.
diff --git a/packages/SystemUI/res/drawable/ic_notify_clear.xml b/tests/Split/assets/statement.xml
index 2163198..91750d1 100644
--- a/packages/SystemUI/res/drawable/ic_notify_clear.xml
+++ b/tests/Split/assets/statement.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,8 +14,6 @@
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true"
- android:drawable="@drawable/ic_notify_clear_normal" />
- <item android:drawable="@drawable/ic_notify_clear_normal" />
-</selector>
+<statement>
+ <value>Hello</value>
+</statement>
diff --git a/tests/Split/res/layout-fr-sw600dp/main.xml b/tests/Split/res/layout-fr-sw600dp/main.xml
new file mode 100644
index 0000000..2461c8c
--- /dev/null
+++ b/tests/Split/res/layout-fr-sw600dp/main.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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+</FrameLayout>
diff --git a/packages/SystemUI/res/drawable/ic_notifications.xml b/tests/Split/res/layout/main.xml
index 97a7623..36992a2 100644
--- a/packages/SystemUI/res/drawable/ic_notifications.xml
+++ b/tests/Split/res/layout/main.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,10 +14,6 @@
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true"
- android:drawable="@drawable/ic_notify_open_normal" />
- <item
- android:drawable="@drawable/ic_notify_open_normal" />
-</selector>
-
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/drawable/ic_notify_settings.xml b/tests/Split/res/values-de/values.xml
index 9303ca4..26d0507 100644
--- a/packages/SystemUI/res/drawable/ic_notify_settings.xml
+++ b/tests/Split/res/values-de/values.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,10 +14,6 @@
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true"
- android:drawable="@drawable/ic_notify_settings_normal" />
- <item
- android:drawable="@drawable/ic_notify_settings_normal" />
-</selector>
-
+<resources>
+ <string name="test">Achtung!</string>
+</resources>
diff --git a/tests/Split/res/values-fr/values.xml b/tests/Split/res/values-fr/values.xml
new file mode 100644
index 0000000..16532da
--- /dev/null
+++ b/tests/Split/res/values-fr/values.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>
+ <string name="app_title">APK Divisé</string>
+ <string name="test">Bonjour, Monde!</string>
+ <string name="blah">Bleh..</string>
+ <string-array name="lotsofstrings">
+ <item>Hé là</item>
+ <item>Au revoir</item>
+ </string-array>
+</resources>
diff --git a/packages/SystemUI/res/drawable/ic_notify_quicksettings.xml b/tests/Split/res/values-sw600dp/values.xml
index 7cf3175..a8329bb 100644
--- a/packages/SystemUI/res/drawable/ic_notify_quicksettings.xml
+++ b/tests/Split/res/values-sw600dp/values.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,10 +14,6 @@
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true"
- android:drawable="@drawable/ic_notify_quicksettings_normal" />
- <item
- android:drawable="@drawable/ic_notify_quicksettings_normal" />
-</selector>
-
+<resources>
+ <dimen name="width">230dp</dimen>
+</resources>
diff --git a/tests/Split/res/values/values.xml b/tests/Split/res/values/values.xml
new file mode 100644
index 0000000..68edc77
--- /dev/null
+++ b/tests/Split/res/values/values.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.
+-->
+
+<resources>
+ <string name="app_title">Split APK</string>
+ <string name="test">Hello, World!</string>
+ <string name="boom">Boom!</string>
+ <string name="blah">Blah...</string>
+ <string-array name="lotsofstrings">
+ <item>Hello there</item>
+ <item>Good bye</item>
+ </string-array>
+
+ <plurals name="plur">
+ <item quantity="zero">I no haz :(</item>
+ <item quantity="one">I haz 1!1! :)</item>
+ <item quantity="many">I haz ALL!</item>
+ </plurals>
+
+ <bool name="que">true</bool>
+ <color name="green">#00FF00</color>
+ <dimen name="width">23dp</dimen>
+ <item type="id" name="identifier" />
+ <integer name="number">123</integer>
+ <integer-array name="numList">
+ <item>1234</item>
+ </integer-array>
+
+ <array name="ary">
+ <item>@string/test</item>
+ <item>@string/boom</item>
+ <item>25dp</item>
+ </array>
+</resources>
diff --git a/tests/Split/src/java/com/android/example/split/ActivityMain.java b/tests/Split/src/java/com/android/example/split/ActivityMain.java
new file mode 100644
index 0000000..a15fb3c
--- /dev/null
+++ b/tests/Split/src/java/com/android/example/split/ActivityMain.java
@@ -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 com.android.example.split;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class ActivityMain extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ TextView text = new TextView(this);
+ text.setText(R.string.test);
+ setContentView(text);
+ }
+}
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index e0dab78..12d5389 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -3,8 +3,10 @@
//
#include "AaptAssets.h"
-#include "ResourceFilter.h"
+#include "AaptConfig.h"
+#include "AaptUtil.h"
#include "Main.h"
+#include "ResourceFilter.h"
#include <utils/misc.h>
#include <utils/SortedVector.h>
@@ -14,7 +16,6 @@
#include <errno.h>
static const char* kDefaultLocale = "default";
-static const char* kWildcardName = "any";
static const char* kAssetDir = "assets";
static const char* kResourceDir = "res";
static const char* kValuesDir = "values";
@@ -149,24 +150,6 @@ static bool isHidden(const char *root, const char *path)
// =========================================================================
// =========================================================================
-/* static */ void AaptLocaleValue::splitAndLowerCase(const char* const chars,
- Vector<String8>* parts, const char separator) {
- const char *p = chars;
- const char *q;
- while (NULL != (q = strchr(p, separator))) {
- String8 val(p, q - p);
- val.toLower();
- parts->add(val);
- p = q+1;
- }
-
- if (p < chars + strlen(chars)) {
- String8 val(p);
- val.toLower();
- parts->add(val);
- }
-}
-
/* static */
inline bool isAlpha(const String8& string) {
const size_t length = string.length();
@@ -230,8 +213,7 @@ void AaptLocaleValue::setVariant(const char* variantChars) {
bool AaptLocaleValue::initFromFilterString(const String8& str) {
// A locale (as specified in the filter) is an underscore separated name such
// as "en_US", "en_Latn_US", or "en_US_POSIX".
- Vector<String8> parts;
- splitAndLowerCase(str.string(), &parts, '_');
+ Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_');
const int numTags = parts.size();
bool valid = false;
@@ -301,8 +283,7 @@ int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int sta
if (part[0] == 'b' && part[1] == '+') {
// This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
// except that the separator is "+" and not "-".
- Vector<String8> subtags;
- AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+');
+ Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+');
subtags.removeItemsAt(0);
if (subtags.size() == 1) {
setLanguage(subtags[0]);
@@ -438,1349 +419,46 @@ void AaptLocaleValue::writeTo(ResTable_config* out) const {
}
}
-
-/* static */ bool
-AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value)
-{
- ResTable_config config;
- memset(&config, 0, sizeof(ResTable_config));
-
- // IMSI - MCC
- if (getMccName(part.string(), &config)) {
- *axis = AXIS_MCC;
- value->intValue = config.mcc;
- return true;
- }
-
- // IMSI - MNC
- if (getMncName(part.string(), &config)) {
- *axis = AXIS_MNC;
- value->intValue = config.mnc;
- return true;
- }
-
- // locale - language
- if (value->localeValue.initFromFilterString(part)) {
- *axis = AXIS_LOCALE;
- return true;
- }
-
- // layout direction
- if (getLayoutDirectionName(part.string(), &config)) {
- *axis = AXIS_LAYOUTDIR;
- value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
- return true;
- }
-
- // smallest screen dp width
- if (getSmallestScreenWidthDpName(part.string(), &config)) {
- *axis = AXIS_SMALLESTSCREENWIDTHDP;
- value->intValue = config.smallestScreenWidthDp;
- return true;
- }
-
- // screen dp width
- if (getScreenWidthDpName(part.string(), &config)) {
- *axis = AXIS_SCREENWIDTHDP;
- value->intValue = config.screenWidthDp;
- return true;
- }
-
- // screen dp height
- if (getScreenHeightDpName(part.string(), &config)) {
- *axis = AXIS_SCREENHEIGHTDP;
- value->intValue = config.screenHeightDp;
- return true;
- }
-
- // screen layout size
- if (getScreenLayoutSizeName(part.string(), &config)) {
- *axis = AXIS_SCREENLAYOUTSIZE;
- value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
- return true;
- }
-
- // screen layout long
- if (getScreenLayoutLongName(part.string(), &config)) {
- *axis = AXIS_SCREENLAYOUTLONG;
- value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
- return true;
- }
-
- // orientation
- if (getOrientationName(part.string(), &config)) {
- *axis = AXIS_ORIENTATION;
- value->intValue = config.orientation;
- return true;
- }
-
- // ui mode type
- if (getUiModeTypeName(part.string(), &config)) {
- *axis = AXIS_UIMODETYPE;
- value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
- return true;
- }
-
- // ui mode night
- if (getUiModeNightName(part.string(), &config)) {
- *axis = AXIS_UIMODENIGHT;
- value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
- return true;
- }
-
- // density
- if (getDensityName(part.string(), &config)) {
- *axis = AXIS_DENSITY;
- value->intValue = config.density;
- return true;
- }
-
- // touchscreen
- if (getTouchscreenName(part.string(), &config)) {
- *axis = AXIS_TOUCHSCREEN;
- value->intValue = config.touchscreen;
- return true;
- }
-
- // keyboard hidden
- if (getKeysHiddenName(part.string(), &config)) {
- *axis = AXIS_KEYSHIDDEN;
- value->intValue = config.inputFlags;
- return true;
- }
-
- // keyboard
- if (getKeyboardName(part.string(), &config)) {
- *axis = AXIS_KEYBOARD;
- value->intValue = config.keyboard;
- return true;
- }
-
- // navigation hidden
- if (getNavHiddenName(part.string(), &config)) {
- *axis = AXIS_NAVHIDDEN;
- value->intValue = config.inputFlags;
- return 0;
- }
-
- // navigation
- if (getNavigationName(part.string(), &config)) {
- *axis = AXIS_NAVIGATION;
- value->intValue = config.navigation;
- return true;
- }
-
- // screen size
- if (getScreenSizeName(part.string(), &config)) {
- *axis = AXIS_SCREENSIZE;
- value->intValue = config.screenSize;
- return true;
- }
-
- // version
- if (getVersionName(part.string(), &config)) {
- *axis = AXIS_VERSION;
- value->intValue = config.version;
- return true;
- }
-
- return false;
-}
-
-AxisValue
-AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
-{
- AxisValue value;
- switch (axis) {
- case AXIS_MCC:
- value.intValue = config.mcc;
- break;
- case AXIS_MNC:
- value.intValue = config.mnc;
- break;
- case AXIS_LOCALE:
- value.localeValue.initFromResTable(config);
- break;
- case AXIS_LAYOUTDIR:
- value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
- break;
- case AXIS_SCREENLAYOUTSIZE:
- value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE;
- break;
- case AXIS_ORIENTATION:
- value.intValue = config.orientation;
- break;
- case AXIS_UIMODETYPE:
- value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
- break;
- case AXIS_UIMODENIGHT:
- value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
- break;
- case AXIS_DENSITY:
- value.intValue = config.density;
- break;
- case AXIS_TOUCHSCREEN:
- value.intValue = config.touchscreen;
- break;
- case AXIS_KEYSHIDDEN:
- value.intValue = config.inputFlags;
- break;
- case AXIS_KEYBOARD:
- value.intValue = config.keyboard;
- break;
- case AXIS_NAVIGATION:
- value.intValue = config.navigation;
- break;
- case AXIS_SCREENSIZE:
- value.intValue = config.screenSize;
- break;
- case AXIS_SMALLESTSCREENWIDTHDP:
- value.intValue = config.smallestScreenWidthDp;
- break;
- case AXIS_SCREENWIDTHDP:
- value.intValue = config.screenWidthDp;
- break;
- case AXIS_SCREENHEIGHTDP:
- value.intValue = config.screenHeightDp;
- break;
- case AXIS_VERSION:
- value.intValue = config.version;
- break;
- }
-
- return value;
-}
-
-bool
-AaptGroupEntry::configSameExcept(const ResTable_config& config,
- const ResTable_config& otherConfig, int axis)
-{
- for (int i=AXIS_START; i<=AXIS_END; i++) {
- if (i == axis) {
- continue;
- }
- if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
- return false;
- }
- }
- return true;
-}
-
bool
AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
{
- mParamsChanged = true;
-
- Vector<String8> parts;
- AaptLocaleValue::splitAndLowerCase(dir, &parts, '-');
-
- String8 mcc, mnc, layoutsize, layoutlong, orient, den;
- String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
- String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
-
- AaptLocaleValue locale;
- int numLocaleComponents = 0;
-
- const int N = parts.size();
- int index = 0;
- String8 part = parts[index];
-
- // resource type
- if (!isValidResourceType(part)) {
- return false;
- }
- *resType = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
-
- // imsi - mcc
- if (getMccName(part.string())) {
- mcc = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not mcc: %s\n", part.string());
- }
-
- // imsi - mnc
- if (getMncName(part.string())) {
- mnc = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
+ const char* q = strchr(dir, '-');
+ size_t typeLen;
+ if (q != NULL) {
+ typeLen = q - dir;
} else {
- //printf("not mnc: %s\n", part.string());
+ typeLen = strlen(dir);
}
- index = locale.initFromDirName(parts, index);
- if (index == -1) {
+ String8 type(dir, typeLen);
+ if (!isValidResourceType(type)) {
return false;
}
- if (index >= N){
- goto success;
- }
-
- part = parts[index];
- if (getLayoutDirectionName(part.string())) {
- layoutDir = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not layout direction: %s\n", part.string());
- }
-
- if (getSmallestScreenWidthDpName(part.string())) {
- smallestwidthdp = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not smallest screen width dp: %s\n", part.string());
- }
-
- if (getScreenWidthDpName(part.string())) {
- widthdp = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen width dp: %s\n", part.string());
- }
-
- if (getScreenHeightDpName(part.string())) {
- heightdp = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen height dp: %s\n", part.string());
- }
-
- if (getScreenLayoutSizeName(part.string())) {
- layoutsize = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen layout size: %s\n", part.string());
- }
-
- if (getScreenLayoutLongName(part.string())) {
- layoutlong = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen layout long: %s\n", part.string());
- }
-
- // orientation
- if (getOrientationName(part.string())) {
- orient = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not orientation: %s\n", part.string());
- }
-
- // ui mode type
- if (getUiModeTypeName(part.string())) {
- uiModeType = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not ui mode type: %s\n", part.string());
- }
-
- // ui mode night
- if (getUiModeNightName(part.string())) {
- uiModeNight = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not ui mode night: %s\n", part.string());
- }
-
- // density
- if (getDensityName(part.string())) {
- den = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not density: %s\n", part.string());
- }
-
- // touchscreen
- if (getTouchscreenName(part.string())) {
- touch = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not touchscreen: %s\n", part.string());
- }
-
- // keyboard hidden
- if (getKeysHiddenName(part.string())) {
- keysHidden = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not keysHidden: %s\n", part.string());
- }
- // keyboard
- if (getKeyboardName(part.string())) {
- key = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not keyboard: %s\n", part.string());
- }
-
- // navigation hidden
- if (getNavHiddenName(part.string())) {
- navHidden = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not navHidden: %s\n", part.string());
- }
-
- if (getNavigationName(part.string())) {
- nav = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not navigation: %s\n", part.string());
- }
-
- if (getScreenSizeName(part.string())) {
- size = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not screen size: %s\n", part.string());
- }
-
- if (getVersionName(part.string())) {
- vers = part;
-
- index++;
- if (index == N) {
- goto success;
+ if (q != NULL) {
+ if (!AaptConfig::parse(String8(q + 1), &mParams)) {
+ return false;
}
- part = parts[index];
- } else {
- //printf("not version: %s\n", part.string());
}
- // if there are extra parts, it doesn't match
- return false;
-
-success:
- this->mcc = mcc;
- this->mnc = mnc;
- this->locale = locale;
- this->screenLayoutSize = layoutsize;
- this->screenLayoutLong = layoutlong;
- this->smallestScreenWidthDp = smallestwidthdp;
- this->screenWidthDp = widthdp;
- this->screenHeightDp = heightdp;
- this->orientation = orient;
- this->uiModeType = uiModeType;
- this->uiModeNight = uiModeNight;
- this->density = den;
- this->touchscreen = touch;
- this->keysHidden = keysHidden;
- this->keyboard = key;
- this->navHidden = navHidden;
- this->navigation = nav;
- this->screenSize = size;
- this->layoutDirection = layoutDir;
- this->version = vers;
-
- // what is this anyway?
- this->vendor = "";
-
+ *resType = type;
return true;
}
String8
-AaptGroupEntry::toString() const
-{
- String8 s = this->mcc;
- s += ",";
- s += this->mnc;
- s += ",";
- s += locale.toDirName();
- s += ",";
- s += layoutDirection;
- s += ",";
- s += smallestScreenWidthDp;
- s += ",";
- s += screenWidthDp;
- s += ",";
- s += screenHeightDp;
- s += ",";
- s += screenLayoutSize;
- s += ",";
- s += screenLayoutLong;
- s += ",";
- s += this->orientation;
- s += ",";
- s += uiModeType;
- s += ",";
- s += uiModeNight;
- s += ",";
- s += density;
- s += ",";
- s += touchscreen;
- s += ",";
- s += keysHidden;
- s += ",";
- s += keyboard;
- s += ",";
- s += navHidden;
- s += ",";
- s += navigation;
- s += ",";
- s += screenSize;
- s += ",";
- s += version;
- return s;
-}
-
-String8
AaptGroupEntry::toDirName(const String8& resType) const
{
String8 s = resType;
- if (this->mcc != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += mcc;
- }
- if (this->mnc != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += mnc;
- }
-
- const String8 localeComponent = locale.toDirName();
- if (localeComponent != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += localeComponent;
- }
-
- if (this->layoutDirection != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += layoutDirection;
- }
- if (this->smallestScreenWidthDp != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += smallestScreenWidthDp;
- }
- if (this->screenWidthDp != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenWidthDp;
- }
- if (this->screenHeightDp != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenHeightDp;
- }
- if (this->screenLayoutSize != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenLayoutSize;
- }
- if (this->screenLayoutLong != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenLayoutLong;
- }
- if (this->orientation != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += orientation;
- }
- if (this->uiModeType != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += uiModeType;
- }
- if (this->uiModeNight != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += uiModeNight;
- }
- if (this->density != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += density;
- }
- if (this->touchscreen != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += touchscreen;
- }
- if (this->keysHidden != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += keysHidden;
- }
- if (this->keyboard != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += keyboard;
- }
- if (this->navHidden != "") {
+ String8 params = mParams.toString();
+ if (params.length() > 0) {
if (s.length() > 0) {
s += "-";
}
- s += navHidden;
+ s += params;
}
- if (this->navigation != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += navigation;
- }
- if (this->screenSize != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += screenSize;
- }
- if (this->version != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += version;
- }
-
return s;
}
-bool AaptGroupEntry::getMccName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->mcc = 0;
- return true;
- }
- const char* c = name;
- if (tolower(*c) != 'm') return false;
- c++;
- if (tolower(*c) != 'c') return false;
- c++;
- if (tolower(*c) != 'c') return false;
- c++;
-
- const char* val = c;
-
- while (*c >= '0' && *c <= '9') {
- c++;
- }
- if (*c != 0) return false;
- if (c-val != 3) return false;
-
- int d = atoi(val);
- if (d != 0) {
- if (out) out->mcc = d;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getMncName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->mcc = 0;
- return true;
- }
- const char* c = name;
- if (tolower(*c) != 'm') return false;
- c++;
- if (tolower(*c) != 'n') return false;
- c++;
- if (tolower(*c) != 'c') return false;
- c++;
-
- const char* val = c;
-
- while (*c >= '0' && *c <= '9') {
- c++;
- }
- if (*c != 0) return false;
- if (c-val == 0 || c-val > 3) return false;
-
- if (out) {
- out->mnc = atoi(val);
- if (out->mnc == 0) {
- out->mnc = ACONFIGURATION_MNC_ZERO;
- }
- }
-
- return true;
-}
-
-bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
- | ResTable_config::LAYOUTDIR_ANY;
- return true;
- } else if (strcmp(name, "ldltr") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
- | ResTable_config::LAYOUTDIR_LTR;
- return true;
- } else if (strcmp(name, "ldrtl") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
- | ResTable_config::LAYOUTDIR_RTL;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_ANY;
- return true;
- } else if (strcmp(name, "small") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_SMALL;
- return true;
- } else if (strcmp(name, "normal") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_NORMAL;
- return true;
- } else if (strcmp(name, "large") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_LARGE;
- return true;
- } else if (strcmp(name, "xlarge") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
- | ResTable_config::SCREENSIZE_XLARGE;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
- | ResTable_config::SCREENLONG_ANY;
- return true;
- } else if (strcmp(name, "long") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
- | ResTable_config::SCREENLONG_YES;
- return true;
- } else if (strcmp(name, "notlong") == 0) {
- if (out) out->screenLayout =
- (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
- | ResTable_config::SCREENLONG_NO;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getOrientationName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->orientation = out->ORIENTATION_ANY;
- return true;
- } else if (strcmp(name, "port") == 0) {
- if (out) out->orientation = out->ORIENTATION_PORT;
- return true;
- } else if (strcmp(name, "land") == 0) {
- if (out) out->orientation = out->ORIENTATION_LAND;
- return true;
- } else if (strcmp(name, "square") == 0) {
- if (out) out->orientation = out->ORIENTATION_SQUARE;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getUiModeTypeName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_ANY;
- return true;
- } else if (strcmp(name, "desk") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_DESK;
- return true;
- } else if (strcmp(name, "car") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_CAR;
- return true;
- } else if (strcmp(name, "television") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_TELEVISION;
- return true;
- } else if (strcmp(name, "appliance") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_APPLIANCE;
- return true;
- } else if (strcmp(name, "watch") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
- | ResTable_config::UI_MODE_TYPE_WATCH;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getUiModeNightName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
- | ResTable_config::UI_MODE_NIGHT_ANY;
- return true;
- } else if (strcmp(name, "night") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
- | ResTable_config::UI_MODE_NIGHT_YES;
- return true;
- } else if (strcmp(name, "notnight") == 0) {
- if (out) out->uiMode =
- (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
- | ResTable_config::UI_MODE_NIGHT_NO;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getDensityName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->density = ResTable_config::DENSITY_DEFAULT;
- return true;
- }
-
- if (strcmp(name, "nodpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_NONE;
- return true;
- }
-
- if (strcmp(name, "ldpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_LOW;
- return true;
- }
-
- if (strcmp(name, "mdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_MEDIUM;
- return true;
- }
-
- if (strcmp(name, "tvdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_TV;
- return true;
- }
-
- if (strcmp(name, "hdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_HIGH;
- return true;
- }
-
- if (strcmp(name, "xhdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_XHIGH;
- return true;
- }
-
- if (strcmp(name, "xxhdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_XXHIGH;
- return true;
- }
-
- if (strcmp(name, "xxxhdpi") == 0) {
- if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
- return true;
- }
-
- char* c = (char*)name;
- while (*c >= '0' && *c <= '9') {
- c++;
- }
-
- // check that we have 'dpi' after the last digit.
- if (toupper(c[0]) != 'D' ||
- toupper(c[1]) != 'P' ||
- toupper(c[2]) != 'I' ||
- c[3] != 0) {
- return false;
- }
-
- // temporarily replace the first letter with \0 to
- // use atoi.
- char tmp = c[0];
- c[0] = '\0';
-
- int d = atoi(name);
- c[0] = tmp;
-
- if (d != 0) {
- if (out) out->density = d;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getTouchscreenName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
- return true;
- } else if (strcmp(name, "notouch") == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
- return true;
- } else if (strcmp(name, "stylus") == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
- return true;
- } else if (strcmp(name, "finger") == 0) {
- if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getKeysHiddenName(const char* name,
- ResTable_config* out)
-{
- uint8_t mask = 0;
- uint8_t value = 0;
- if (strcmp(name, kWildcardName) == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_ANY;
- } else if (strcmp(name, "keysexposed") == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_NO;
- } else if (strcmp(name, "keyshidden") == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_YES;
- } else if (strcmp(name, "keyssoft") == 0) {
- mask = ResTable_config::MASK_KEYSHIDDEN;
- value = ResTable_config::KEYSHIDDEN_SOFT;
- }
-
- if (mask != 0) {
- if (out) out->inputFlags = (out->inputFlags&~mask) | value;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getKeyboardName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->keyboard = out->KEYBOARD_ANY;
- return true;
- } else if (strcmp(name, "nokeys") == 0) {
- if (out) out->keyboard = out->KEYBOARD_NOKEYS;
- return true;
- } else if (strcmp(name, "qwerty") == 0) {
- if (out) out->keyboard = out->KEYBOARD_QWERTY;
- return true;
- } else if (strcmp(name, "12key") == 0) {
- if (out) out->keyboard = out->KEYBOARD_12KEY;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getNavHiddenName(const char* name,
- ResTable_config* out)
-{
- uint8_t mask = 0;
- uint8_t value = 0;
- if (strcmp(name, kWildcardName) == 0) {
- mask = ResTable_config::MASK_NAVHIDDEN;
- value = ResTable_config::NAVHIDDEN_ANY;
- } else if (strcmp(name, "navexposed") == 0) {
- mask = ResTable_config::MASK_NAVHIDDEN;
- value = ResTable_config::NAVHIDDEN_NO;
- } else if (strcmp(name, "navhidden") == 0) {
- mask = ResTable_config::MASK_NAVHIDDEN;
- value = ResTable_config::NAVHIDDEN_YES;
- }
-
- if (mask != 0) {
- if (out) out->inputFlags = (out->inputFlags&~mask) | value;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getNavigationName(const char* name,
- ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) out->navigation = out->NAVIGATION_ANY;
- return true;
- } else if (strcmp(name, "nonav") == 0) {
- if (out) out->navigation = out->NAVIGATION_NONAV;
- return true;
- } else if (strcmp(name, "dpad") == 0) {
- if (out) out->navigation = out->NAVIGATION_DPAD;
- return true;
- } else if (strcmp(name, "trackball") == 0) {
- if (out) out->navigation = out->NAVIGATION_TRACKBALL;
- return true;
- } else if (strcmp(name, "wheel") == 0) {
- if (out) out->navigation = out->NAVIGATION_WHEEL;
- return true;
- }
-
- return false;
-}
-
-bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->screenWidth = out->SCREENWIDTH_ANY;
- out->screenHeight = out->SCREENHEIGHT_ANY;
- }
- return true;
- }
-
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || *x != 'x') return false;
- String8 xName(name, x-name);
- x++;
-
- const char* y = x;
- while (*y >= '0' && *y <= '9') y++;
- if (y == name || *y != 0) return false;
- String8 yName(x, y-x);
-
- uint16_t w = (uint16_t)atoi(xName.string());
- uint16_t h = (uint16_t)atoi(yName.string());
- if (w < h) {
- return false;
- }
-
- if (out) {
- out->screenWidth = w;
- out->screenHeight = h;
- }
-
- return true;
-}
-
-bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
- }
- return true;
- }
-
- if (*name != 's') return false;
- name++;
- if (*name != 'w') return false;
- name++;
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
- String8 xName(name, x-name);
-
- if (out) {
- out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
- }
-
- return true;
-}
-
-bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->screenWidthDp = out->SCREENWIDTH_ANY;
- }
- return true;
- }
-
- if (*name != 'w') return false;
- name++;
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
- String8 xName(name, x-name);
-
- if (out) {
- out->screenWidthDp = (uint16_t)atoi(xName.string());
- }
-
- return true;
-}
-
-bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->screenHeightDp = out->SCREENWIDTH_ANY;
- }
- return true;
- }
-
- if (*name != 'h') return false;
- name++;
- const char* x = name;
- while (*x >= '0' && *x <= '9') x++;
- if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
- String8 xName(name, x-name);
-
- if (out) {
- out->screenHeightDp = (uint16_t)atoi(xName.string());
- }
-
- return true;
-}
-
-bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
-{
- if (strcmp(name, kWildcardName) == 0) {
- if (out) {
- out->sdkVersion = out->SDKVERSION_ANY;
- out->minorVersion = out->MINORVERSION_ANY;
- }
- return true;
- }
-
- if (*name != 'v') {
- return false;
- }
-
- name++;
- const char* s = name;
- while (*s >= '0' && *s <= '9') s++;
- if (s == name || *s != 0) return false;
- String8 sdkName(name, s-name);
-
- if (out) {
- out->sdkVersion = (uint16_t)atoi(sdkName.string());
- out->minorVersion = 0;
- }
-
- return true;
-}
-
-int AaptGroupEntry::compare(const AaptGroupEntry& o) const
-{
- int v = mcc.compare(o.mcc);
- if (v == 0) v = mnc.compare(o.mnc);
- if (v == 0) v = locale.compare(o.locale);
- if (v == 0) v = layoutDirection.compare(o.layoutDirection);
- if (v == 0) v = vendor.compare(o.vendor);
- if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
- if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
- if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
- if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
- if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
- if (v == 0) v = orientation.compare(o.orientation);
- if (v == 0) v = uiModeType.compare(o.uiModeType);
- if (v == 0) v = uiModeNight.compare(o.uiModeNight);
- if (v == 0) v = density.compare(o.density);
- if (v == 0) v = touchscreen.compare(o.touchscreen);
- if (v == 0) v = keysHidden.compare(o.keysHidden);
- if (v == 0) v = keyboard.compare(o.keyboard);
- if (v == 0) v = navHidden.compare(o.navHidden);
- if (v == 0) v = navigation.compare(o.navigation);
- if (v == 0) v = screenSize.compare(o.screenSize);
- if (v == 0) v = version.compare(o.version);
- return v;
-}
-
-const ResTable_config AaptGroupEntry::toParams() const
-{
- if (!mParamsChanged) {
- return mParams;
- }
-
- mParamsChanged = false;
- ResTable_config& params = mParams;
- memset(&params, 0, sizeof(ResTable_config));
- getMccName(mcc.string(), &params);
- getMncName(mnc.string(), &params);
- locale.writeTo(&params);
- getLayoutDirectionName(layoutDirection.string(), &params);
- getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
- getScreenWidthDpName(screenWidthDp.string(), &params);
- getScreenHeightDpName(screenHeightDp.string(), &params);
- getScreenLayoutSizeName(screenLayoutSize.string(), &params);
- getScreenLayoutLongName(screenLayoutLong.string(), &params);
- getOrientationName(orientation.string(), &params);
- getUiModeTypeName(uiModeType.string(), &params);
- getUiModeNightName(uiModeNight.string(), &params);
- getDensityName(density.string(), &params);
- getTouchscreenName(touchscreen.string(), &params);
- getKeysHiddenName(keysHidden.string(), &params);
- getKeyboardName(keyboard.string(), &params);
- getNavHiddenName(navHidden.string(), &params);
- getNavigationName(navigation.string(), &params);
- getScreenSizeName(screenSize.string(), &params);
- getVersionName(version.string(), &params);
-
- // Fix up version number based on specified parameters.
- int minSdk = 0;
- if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
- || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
- || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
- minSdk = SDK_HONEYCOMB_MR2;
- } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
- != ResTable_config::UI_MODE_TYPE_ANY
- || (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
- != ResTable_config::UI_MODE_NIGHT_ANY) {
- minSdk = SDK_FROYO;
- } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
- != ResTable_config::SCREENSIZE_ANY
- || (params.screenLayout&ResTable_config::MASK_SCREENLONG)
- != ResTable_config::SCREENLONG_ANY
- || params.density != ResTable_config::DENSITY_DEFAULT) {
- minSdk = SDK_DONUT;
- }
-
- if (minSdk > params.sdkVersion) {
- params.sdkVersion = minSdk;
- }
-
- return params;
-}
// =========================================================================
// =========================================================================
@@ -2229,9 +907,7 @@ AaptAssets::AaptAssets()
: AaptDir(String8(), String8()),
mHavePrivateSymbols(false),
mChanged(false), mHaveIncludedAssets(false),
- mRes(NULL)
-{
-}
+ mRes(NULL) {}
const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
if (mChanged) {
@@ -2506,7 +1182,7 @@ ssize_t AaptAssets::slurpResourceTree(Bundle* bundle, const String8& srcDir)
String8 resType;
bool b = group.initFromDirName(entry->d_name, &resType);
if (!b) {
- fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
+ fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(),
entry->d_name);
err = -1;
continue;
@@ -2654,30 +1330,35 @@ bail:
status_t AaptAssets::filter(Bundle* bundle)
{
- ResourceFilter reqFilter;
+ WeakResourceFilter reqFilter;
status_t err = reqFilter.parse(bundle->getConfigurations());
if (err != NO_ERROR) {
return err;
}
- ResourceFilter prefFilter;
- err = prefFilter.parse(bundle->getPreferredConfigurations());
- if (err != NO_ERROR) {
- return err;
+ uint32_t preferredDensity = 0;
+ if (bundle->getPreferredDensity().size() > 0) {
+ ResTable_config preferredConfig;
+ if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) {
+ fprintf(stderr, "Error parsing preferred density: %s\n",
+ bundle->getPreferredDensity().string());
+ return UNKNOWN_ERROR;
+ }
+ preferredDensity = preferredConfig.density;
}
- if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
+ if (reqFilter.isEmpty() && preferredDensity == 0) {
return NO_ERROR;
}
if (bundle->getVerbose()) {
if (!reqFilter.isEmpty()) {
printf("Applying required filter: %s\n",
- bundle->getConfigurations());
+ bundle->getConfigurations().string());
}
- if (!prefFilter.isEmpty()) {
- printf("Applying preferred filter: %s\n",
- bundle->getPreferredConfigurations());
+ if (preferredDensity > 0) {
+ printf("Applying preferred density filter: %s\n",
+ bundle->getPreferredDensity().string());
}
}
@@ -2734,88 +1415,70 @@ status_t AaptAssets::filter(Bundle* bundle)
}
// Quick check: no preferred filters, nothing more to do.
- if (prefFilter.isEmpty()) {
+ if (preferredDensity == 0) {
continue;
}
// Get the preferred density if there is one. We do not match exactly for density.
// If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
// pick xhdpi.
- uint32_t preferredDensity = 0;
- const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
- if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
- preferredDensity = (*preferredConfigs)[0].intValue;
- }
+ for (size_t k=0; k<grp->getFiles().size(); k++) {
+ sp<AaptFile> file = grp->getFiles().valueAt(k);
+ if (k == 0 && grp->getFiles().size() == 1) {
+ // If this is the only file left, we need to keep it.
+ // Otherwise the resource IDs we are using will be inconsistent
+ // with what we get when not stripping. Sucky, but at least
+ // for now we can rely on the back-end doing another filtering
+ // pass to take this out and leave us with this resource name
+ // containing no entries.
+ continue;
+ }
+ if (file->getPath().getPathExtension() == ".xml") {
+ // We can't remove .xml files at this point, because when
+ // we parse them they may add identifier resources, so
+ // removing them can cause our resource identifiers to
+ // become inconsistent.
+ continue;
+ }
+ const ResTable_config& config(file->getGroupEntry().toParams());
+ if (config.density != 0 && config.density != preferredDensity) {
+ // This is a resource we would prefer not to have. Check
+ // to see if have a similar variation that we would like
+ // to have and, if so, we can drop it.
+ uint32_t bestDensity = config.density;
+
+ for (size_t m=0; m<grp->getFiles().size(); m++) {
+ if (m == k) {
+ continue;
+ }
- // Now deal with preferred configurations.
- for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
- for (size_t k=0; k<grp->getFiles().size(); k++) {
- sp<AaptFile> file = grp->getFiles().valueAt(k);
- if (k == 0 && grp->getFiles().size() == 1) {
- // If this is the only file left, we need to keep it.
- // Otherwise the resource IDs we are using will be inconsistent
- // with what we get when not stripping. Sucky, but at least
- // for now we can rely on the back-end doing another filtering
- // pass to take this out and leave us with this resource name
- // containing no entries.
- continue;
- }
- if (file->getPath().getPathExtension() == ".xml") {
- // We can't remove .xml files at this point, because when
- // we parse them they may add identifier resources, so
- // removing them can cause our resource identifiers to
- // become inconsistent.
- continue;
- }
- const ResTable_config& config(file->getGroupEntry().toParams());
- if (!prefFilter.match(axis, config)) {
- // This is a resource we would prefer not to have. Check
- // to see if have a similar variation that we would like
- // to have and, if so, we can drop it.
-
- uint32_t bestDensity = config.density;
-
- for (size_t m=0; m<grp->getFiles().size(); m++) {
- if (m == k) continue;
- sp<AaptFile> mfile = grp->getFiles().valueAt(m);
- const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
- if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
- if (axis == AXIS_DENSITY && preferredDensity > 0) {
- // See if there is a better density resource
- if (mconfig.density < bestDensity &&
- mconfig.density > preferredDensity &&
- bestDensity > preferredDensity) {
- // This density is between our best density and
- // the preferred density, therefore it is better.
- bestDensity = mconfig.density;
- } else if (mconfig.density > bestDensity &&
- bestDensity < preferredDensity) {
- // This density is better than our best density and
- // our best density was smaller than our preferred
- // density, so it is better.
- bestDensity = mconfig.density;
- }
- } else if (prefFilter.match(axis, mconfig)) {
- if (bundle->getVerbose()) {
- printf("Pruning unneeded resource: %s\n",
- file->getPrintableSource().string());
- }
- grp->removeFile(k);
- k--;
- break;
- }
+ sp<AaptFile> mfile = grp->getFiles().valueAt(m);
+ const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
+ if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) {
+ // See if there is a better density resource
+ if (mconfig.density < bestDensity &&
+ mconfig.density > preferredDensity &&
+ bestDensity > preferredDensity) {
+ // This density is between our best density and
+ // the preferred density, therefore it is better.
+ bestDensity = mconfig.density;
+ } else if (mconfig.density > bestDensity &&
+ bestDensity < preferredDensity) {
+ // This density is better than our best density and
+ // our best density was smaller than our preferred
+ // density, so it is better.
+ bestDensity = mconfig.density;
}
}
+ }
- if (axis == AXIS_DENSITY && preferredDensity > 0 &&
- bestDensity != config.density) {
- if (bundle->getVerbose()) {
- printf("Pruning unneeded resource: %s\n",
- file->getPrintableSource().string());
- }
- grp->removeFile(k);
- k--;
+ if (bestDensity != config.density) {
+ if (bundle->getVerbose()) {
+ printf("Pruning unneeded resource: %s\n",
+ file->getPrintableSource().string());
}
+ grp->removeFile(k);
+ k--;
}
}
}
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 82dda5f..0c2576a 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -6,22 +6,24 @@
#ifndef __AAPT_ASSETS_H
#define __AAPT_ASSETS_H
-#include <stdlib.h>
#include <androidfw/AssetManager.h>
#include <androidfw/ResourceTypes.h>
+#include <stdlib.h>
+#include <set>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>
#include <utils/Vector.h>
-#include "ZipFile.h"
+#include "AaptConfig.h"
#include "Bundle.h"
+#include "ConfigDescription.h"
#include "SourcePos.h"
+#include "ZipFile.h"
using namespace android;
-
extern const char * const gDefaultIgnoreAssets;
extern const char * gUserIgnoreAssets;
@@ -82,9 +84,6 @@ struct AaptLocaleValue {
return memcmp(this, &other, sizeof(AaptLocaleValue));
}
- static void splitAndLowerCase(const char* const chars, Vector<String8>* parts,
- const char separator);
-
inline bool operator<(const AaptLocaleValue& o) const { return compare(o) < 0; }
inline bool operator<=(const AaptLocaleValue& o) const { return compare(o) <= 0; }
inline bool operator==(const AaptLocaleValue& o) const { return compare(o) == 0; }
@@ -98,31 +97,6 @@ private:
void setVariant(const char* variant);
};
-struct AxisValue {
- // Used for all axes except AXIS_LOCALE, which is represented
- // as a AaptLocaleValue value.
- int intValue;
- AaptLocaleValue localeValue;
-
- AxisValue() : intValue(0) {
- }
-
- inline int compare(const AxisValue &other) const {
- if (intValue != other.intValue) {
- return intValue - other.intValue;
- }
-
- return localeValue.compare(other.localeValue);
- }
-
- inline bool operator<(const AxisValue& o) const { return compare(o) < 0; }
- inline bool operator<=(const AxisValue& o) const { return compare(o) <= 0; }
- inline bool operator==(const AxisValue& o) const { return compare(o) == 0; }
- inline bool operator!=(const AxisValue& o) const { return compare(o) != 0; }
- inline bool operator>=(const AxisValue& o) const { return compare(o) >= 0; }
- inline bool operator>(const AxisValue& o) const { return compare(o) > 0; }
-};
-
/**
* This structure contains a specific variation of a single file out
* of all the variations it can have that we can have.
@@ -130,23 +104,11 @@ struct AxisValue {
struct AaptGroupEntry
{
public:
- AaptGroupEntry() : mParamsChanged(true) {
- memset(&mParams, 0, sizeof(ResTable_config));
- }
-
bool initFromDirName(const char* dir, String8* resType);
- static bool parseFilterNamePart(const String8& part, int* axis, AxisValue* value);
-
- static AxisValue getConfigValueForAxis(const ResTable_config& config, int axis);
-
- static bool configSameExcept(const ResTable_config& config,
- const ResTable_config& otherConfig, int axis);
-
- int compare(const AaptGroupEntry& o) const;
-
- const ResTable_config toParams() const;
+ inline const ConfigDescription& toParams() const { return mParams; }
+ inline int compare(const AaptGroupEntry& o) const { return mParams.compareLogical(o.mParams); }
inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
@@ -154,56 +116,13 @@ public:
inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
- String8 toString() const;
+ String8 toString() const { return mParams.toString(); }
String8 toDirName(const String8& resType) const;
- const String8& getVersionString() const { return version; }
+ const String8 getVersionString() const { return AaptConfig::getVersion(mParams); }
private:
- static bool getMccName(const char* name, ResTable_config* out = NULL);
- static bool getMncName(const char* name, ResTable_config* out = NULL);
- static bool getScreenLayoutSizeName(const char* name, ResTable_config* out = NULL);
- static bool getScreenLayoutLongName(const char* name, ResTable_config* out = NULL);
- static bool getOrientationName(const char* name, ResTable_config* out = NULL);
- static bool getUiModeTypeName(const char* name, ResTable_config* out = NULL);
- static bool getUiModeNightName(const char* name, ResTable_config* out = NULL);
- static bool getDensityName(const char* name, ResTable_config* out = NULL);
- static bool getTouchscreenName(const char* name, ResTable_config* out = NULL);
- static bool getKeysHiddenName(const char* name, ResTable_config* out = NULL);
- static bool getKeyboardName(const char* name, ResTable_config* out = NULL);
- static bool getNavigationName(const char* name, ResTable_config* out = NULL);
- static bool getNavHiddenName(const char* name, ResTable_config* out = NULL);
- static bool getScreenSizeName(const char* name, ResTable_config* out = NULL);
- static bool getSmallestScreenWidthDpName(const char* name, ResTable_config* out = NULL);
- static bool getScreenWidthDpName(const char* name, ResTable_config* out = NULL);
- static bool getScreenHeightDpName(const char* name, ResTable_config* out = NULL);
- static bool getLayoutDirectionName(const char* name, ResTable_config* out = NULL);
- static bool getVersionName(const char* name, ResTable_config* out = NULL);
-
- String8 mcc;
- String8 mnc;
- AaptLocaleValue locale;
- String8 vendor;
- String8 smallestScreenWidthDp;
- String8 screenWidthDp;
- String8 screenHeightDp;
- String8 screenLayoutSize;
- String8 screenLayoutLong;
- String8 orientation;
- String8 uiModeType;
- String8 uiModeNight;
- String8 density;
- String8 touchscreen;
- String8 keysHidden;
- String8 keyboard;
- String8 navHidden;
- String8 navigation;
- String8 screenSize;
- String8 layoutDirection;
- String8 version;
-
- mutable bool mParamsChanged;
- mutable ResTable_config mParams;
+ ConfigDescription mParams;
};
inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp
new file mode 100644
index 0000000..69a9c7f
--- /dev/null
+++ b/tools/aapt/AaptConfig.cpp
@@ -0,0 +1,790 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <androidfw/ResourceTypes.h>
+#include <ctype.h>
+
+#include "AaptConfig.h"
+#include "AaptAssets.h"
+#include "AaptUtil.h"
+#include "ResourceFilter.h"
+
+using android::String8;
+using android::Vector;
+using android::ResTable_config;
+
+namespace AaptConfig {
+
+static const char* kWildcardName = "any";
+
+bool parse(const String8& str, ConfigDescription* out) {
+ Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '-');
+
+ ConfigDescription config;
+ AaptLocaleValue locale;
+ ssize_t index = 0;
+ ssize_t localeIndex = 0;
+ const ssize_t N = parts.size();
+ const char* part = parts[index].string();
+
+ if (str.length() == 0) {
+ goto success;
+ }
+
+ if (parseMcc(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseMnc(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ // Locale spans a few '-' separators, so we let it
+ // control the index.
+ localeIndex = locale.initFromDirName(parts, index);
+ if (localeIndex < 0) {
+ return false;
+ } else if (localeIndex > index) {
+ locale.writeTo(&config);
+ index = localeIndex;
+ if (index >= N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseLayoutDirection(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseSmallestScreenWidthDp(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseScreenWidthDp(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseScreenHeightDp(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseScreenLayoutSize(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseScreenLayoutLong(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseOrientation(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseUiModeType(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseUiModeNight(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseDensity(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseTouchscreen(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseKeysHidden(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseKeyboard(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseNavHidden(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseNavigation(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseScreenSize(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ if (parseVersion(part, &config)) {
+ index++;
+ if (index == N) {
+ goto success;
+ }
+ part = parts[index].string();
+ }
+
+ // Unrecognized.
+ return false;
+
+success:
+ if (out != NULL) {
+ applyVersionForCompatibility(&config);
+ *out = config;
+ }
+ return true;
+}
+
+bool parseCommaSeparatedList(const String8& str, std::set<ConfigDescription>* outSet) {
+ Vector<String8> parts = AaptUtil::splitAndLowerCase(str, ',');
+ const size_t N = parts.size();
+ for (size_t i = 0; i < N; i++) {
+ ConfigDescription config;
+ if (!parse(parts[i], &config)) {
+ return false;
+ }
+ outSet->insert(config);
+ }
+ return true;
+}
+
+void applyVersionForCompatibility(ConfigDescription* config) {
+ if (config == NULL) {
+ return;
+ }
+
+ uint16_t minSdk = 0;
+ if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
+ || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
+ || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
+ minSdk = SDK_HONEYCOMB_MR2;
+ } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
+ != ResTable_config::UI_MODE_TYPE_ANY
+ || (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT)
+ != ResTable_config::UI_MODE_NIGHT_ANY) {
+ minSdk = SDK_FROYO;
+ } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE)
+ != ResTable_config::SCREENSIZE_ANY
+ || (config->screenLayout & ResTable_config::MASK_SCREENLONG)
+ != ResTable_config::SCREENLONG_ANY
+ || config->density != ResTable_config::DENSITY_DEFAULT) {
+ minSdk = SDK_DONUT;
+ }
+
+ if (minSdk > config->sdkVersion) {
+ config->sdkVersion = minSdk;
+ }
+}
+
+bool parseMcc(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->mcc = 0;
+ return true;
+ }
+ const char* c = name;
+ if (tolower(*c) != 'm') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+
+ const char* val = c;
+
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
+ if (*c != 0) return false;
+ if (c-val != 3) return false;
+
+ int d = atoi(val);
+ if (d != 0) {
+ if (out) out->mcc = d;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseMnc(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->mcc = 0;
+ return true;
+ }
+ const char* c = name;
+ if (tolower(*c) != 'm') return false;
+ c++;
+ if (tolower(*c) != 'n') return false;
+ c++;
+ if (tolower(*c) != 'c') return false;
+ c++;
+
+ const char* val = c;
+
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
+ if (*c != 0) return false;
+ if (c-val == 0 || c-val > 3) return false;
+
+ if (out) {
+ out->mnc = atoi(val);
+ if (out->mnc == 0) {
+ out->mnc = ACONFIGURATION_MNC_ZERO;
+ }
+ }
+
+ return true;
+}
+
+bool parseLayoutDirection(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_ANY;
+ return true;
+ } else if (strcmp(name, "ldltr") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_LTR;
+ return true;
+ } else if (strcmp(name, "ldrtl") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+ | ResTable_config::LAYOUTDIR_RTL;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseScreenLayoutSize(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | ResTable_config::SCREENSIZE_ANY;
+ return true;
+ } else if (strcmp(name, "small") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | ResTable_config::SCREENSIZE_SMALL;
+ return true;
+ } else if (strcmp(name, "normal") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | ResTable_config::SCREENSIZE_NORMAL;
+ return true;
+ } else if (strcmp(name, "large") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | ResTable_config::SCREENSIZE_LARGE;
+ return true;
+ } else if (strcmp(name, "xlarge") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+ | ResTable_config::SCREENSIZE_XLARGE;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
+ | ResTable_config::SCREENLONG_ANY;
+ return true;
+ } else if (strcmp(name, "long") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
+ | ResTable_config::SCREENLONG_YES;
+ return true;
+ } else if (strcmp(name, "notlong") == 0) {
+ if (out) out->screenLayout =
+ (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
+ | ResTable_config::SCREENLONG_NO;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseOrientation(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->orientation = out->ORIENTATION_ANY;
+ return true;
+ } else if (strcmp(name, "port") == 0) {
+ if (out) out->orientation = out->ORIENTATION_PORT;
+ return true;
+ } else if (strcmp(name, "land") == 0) {
+ if (out) out->orientation = out->ORIENTATION_LAND;
+ return true;
+ } else if (strcmp(name, "square") == 0) {
+ if (out) out->orientation = out->ORIENTATION_SQUARE;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseUiModeType(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_ANY;
+ return true;
+ } else if (strcmp(name, "desk") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_DESK;
+ return true;
+ } else if (strcmp(name, "car") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_CAR;
+ return true;
+ } else if (strcmp(name, "television") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_TELEVISION;
+ return true;
+ } else if (strcmp(name, "appliance") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_APPLIANCE;
+ return true;
+ } else if (strcmp(name, "watch") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+ | ResTable_config::UI_MODE_TYPE_WATCH;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseUiModeNight(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+ | ResTable_config::UI_MODE_NIGHT_ANY;
+ return true;
+ } else if (strcmp(name, "night") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+ | ResTable_config::UI_MODE_NIGHT_YES;
+ return true;
+ } else if (strcmp(name, "notnight") == 0) {
+ if (out) out->uiMode =
+ (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+ | ResTable_config::UI_MODE_NIGHT_NO;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseDensity(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->density = ResTable_config::DENSITY_DEFAULT;
+ return true;
+ }
+
+ if (strcmp(name, "nodpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_NONE;
+ return true;
+ }
+
+ if (strcmp(name, "ldpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_LOW;
+ return true;
+ }
+
+ if (strcmp(name, "mdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_MEDIUM;
+ return true;
+ }
+
+ if (strcmp(name, "tvdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_TV;
+ return true;
+ }
+
+ if (strcmp(name, "hdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_HIGH;
+ return true;
+ }
+
+ if (strcmp(name, "xhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XHIGH;
+ return true;
+ }
+
+ if (strcmp(name, "xxhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XXHIGH;
+ return true;
+ }
+
+ if (strcmp(name, "xxxhdpi") == 0) {
+ if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
+ return true;
+ }
+
+ char* c = (char*)name;
+ while (*c >= '0' && *c <= '9') {
+ c++;
+ }
+
+ // check that we have 'dpi' after the last digit.
+ if (toupper(c[0]) != 'D' ||
+ toupper(c[1]) != 'P' ||
+ toupper(c[2]) != 'I' ||
+ c[3] != 0) {
+ return false;
+ }
+
+ // temporarily replace the first letter with \0 to
+ // use atoi.
+ char tmp = c[0];
+ c[0] = '\0';
+
+ int d = atoi(name);
+ c[0] = tmp;
+
+ if (d != 0) {
+ if (out) out->density = d;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseTouchscreen(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
+ return true;
+ } else if (strcmp(name, "notouch") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
+ return true;
+ } else if (strcmp(name, "stylus") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
+ return true;
+ } else if (strcmp(name, "finger") == 0) {
+ if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseKeysHidden(const char* name, ResTable_config* out) {
+ uint8_t mask = 0;
+ uint8_t value = 0;
+ if (strcmp(name, kWildcardName) == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_ANY;
+ } else if (strcmp(name, "keysexposed") == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_NO;
+ } else if (strcmp(name, "keyshidden") == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_YES;
+ } else if (strcmp(name, "keyssoft") == 0) {
+ mask = ResTable_config::MASK_KEYSHIDDEN;
+ value = ResTable_config::KEYSHIDDEN_SOFT;
+ }
+
+ if (mask != 0) {
+ if (out) out->inputFlags = (out->inputFlags&~mask) | value;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseKeyboard(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->keyboard = out->KEYBOARD_ANY;
+ return true;
+ } else if (strcmp(name, "nokeys") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_NOKEYS;
+ return true;
+ } else if (strcmp(name, "qwerty") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_QWERTY;
+ return true;
+ } else if (strcmp(name, "12key") == 0) {
+ if (out) out->keyboard = out->KEYBOARD_12KEY;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseNavHidden(const char* name, ResTable_config* out) {
+ uint8_t mask = 0;
+ uint8_t value = 0;
+ if (strcmp(name, kWildcardName) == 0) {
+ mask = ResTable_config::MASK_NAVHIDDEN;
+ value = ResTable_config::NAVHIDDEN_ANY;
+ } else if (strcmp(name, "navexposed") == 0) {
+ mask = ResTable_config::MASK_NAVHIDDEN;
+ value = ResTable_config::NAVHIDDEN_NO;
+ } else if (strcmp(name, "navhidden") == 0) {
+ mask = ResTable_config::MASK_NAVHIDDEN;
+ value = ResTable_config::NAVHIDDEN_YES;
+ }
+
+ if (mask != 0) {
+ if (out) out->inputFlags = (out->inputFlags&~mask) | value;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseNavigation(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) out->navigation = out->NAVIGATION_ANY;
+ return true;
+ } else if (strcmp(name, "nonav") == 0) {
+ if (out) out->navigation = out->NAVIGATION_NONAV;
+ return true;
+ } else if (strcmp(name, "dpad") == 0) {
+ if (out) out->navigation = out->NAVIGATION_DPAD;
+ return true;
+ } else if (strcmp(name, "trackball") == 0) {
+ if (out) out->navigation = out->NAVIGATION_TRACKBALL;
+ return true;
+ } else if (strcmp(name, "wheel") == 0) {
+ if (out) out->navigation = out->NAVIGATION_WHEEL;
+ return true;
+ }
+
+ return false;
+}
+
+bool parseScreenSize(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->screenWidth = out->SCREENWIDTH_ANY;
+ out->screenHeight = out->SCREENHEIGHT_ANY;
+ }
+ return true;
+ }
+
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || *x != 'x') return false;
+ String8 xName(name, x-name);
+ x++;
+
+ const char* y = x;
+ while (*y >= '0' && *y <= '9') y++;
+ if (y == name || *y != 0) return false;
+ String8 yName(x, y-x);
+
+ uint16_t w = (uint16_t)atoi(xName.string());
+ uint16_t h = (uint16_t)atoi(yName.string());
+ if (w < h) {
+ return false;
+ }
+
+ if (out) {
+ out->screenWidth = w;
+ out->screenHeight = h;
+ }
+
+ return true;
+}
+
+bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
+ }
+ return true;
+ }
+
+ if (*name != 's') return false;
+ name++;
+ if (*name != 'w') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ String8 xName(name, x-name);
+
+ if (out) {
+ out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
+ }
+
+ return true;
+}
+
+bool parseScreenWidthDp(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->screenWidthDp = out->SCREENWIDTH_ANY;
+ }
+ return true;
+ }
+
+ if (*name != 'w') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ String8 xName(name, x-name);
+
+ if (out) {
+ out->screenWidthDp = (uint16_t)atoi(xName.string());
+ }
+
+ return true;
+}
+
+bool parseScreenHeightDp(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->screenHeightDp = out->SCREENWIDTH_ANY;
+ }
+ return true;
+ }
+
+ if (*name != 'h') return false;
+ name++;
+ const char* x = name;
+ while (*x >= '0' && *x <= '9') x++;
+ if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+ String8 xName(name, x-name);
+
+ if (out) {
+ out->screenHeightDp = (uint16_t)atoi(xName.string());
+ }
+
+ return true;
+}
+
+bool parseVersion(const char* name, ResTable_config* out) {
+ if (strcmp(name, kWildcardName) == 0) {
+ if (out) {
+ out->sdkVersion = out->SDKVERSION_ANY;
+ out->minorVersion = out->MINORVERSION_ANY;
+ }
+ return true;
+ }
+
+ if (*name != 'v') {
+ return false;
+ }
+
+ name++;
+ const char* s = name;
+ while (*s >= '0' && *s <= '9') s++;
+ if (s == name || *s != 0) return false;
+ String8 sdkName(name, s-name);
+
+ if (out) {
+ out->sdkVersion = (uint16_t)atoi(sdkName.string());
+ out->minorVersion = 0;
+ }
+
+ return true;
+}
+
+String8 getVersion(const ResTable_config& config) {
+ return String8::format("v%u", config.sdkVersion);
+}
+
+bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) {
+ return a.diff(b) == axisMask;
+}
+
+} // namespace AaptConfig
diff --git a/tools/aapt/AaptConfig.h b/tools/aapt/AaptConfig.h
new file mode 100644
index 0000000..2963539
--- /dev/null
+++ b/tools/aapt/AaptConfig.h
@@ -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.
+ */
+
+#ifndef __AAPT_CONFIG_H
+#define __AAPT_CONFIG_H
+
+#include <set>
+#include <utils/String8.h>
+
+#include "ConfigDescription.h"
+
+/**
+ * Utility methods for dealing with configurations.
+ */
+namespace AaptConfig {
+
+/**
+ * Parse a string of the form 'fr-sw600dp-land' and fill in the
+ * given ResTable_config with resulting configuration parameters.
+ *
+ * The resulting configuration has the appropriate sdkVersion defined
+ * for backwards compatibility.
+ */
+bool parse(const android::String8& str, ConfigDescription* out = NULL);
+
+/**
+ * Parse a comma separated list of configuration strings. Duplicate configurations
+ * will be removed.
+ *
+ * Example input: "fr,de-land,fr-sw600dp-land"
+ */
+bool parseCommaSeparatedList(const android::String8& str, std::set<ConfigDescription>* outSet);
+
+/**
+ * If the configuration uses an axis that was added after
+ * the original Android release, make sure the SDK version
+ * is set accordingly.
+ */
+void applyVersionForCompatibility(ConfigDescription* config);
+
+// Individual axis
+bool parseMcc(const char* str, android::ResTable_config* out = NULL);
+bool parseMnc(const char* str, android::ResTable_config* out = NULL);
+bool parseLayoutDirection(const char* str, android::ResTable_config* out = NULL);
+bool parseSmallestScreenWidthDp(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenWidthDp(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenHeightDp(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenLayoutSize(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenLayoutLong(const char* str, android::ResTable_config* out = NULL);
+bool parseOrientation(const char* str, android::ResTable_config* out = NULL);
+bool parseUiModeType(const char* str, android::ResTable_config* out = NULL);
+bool parseUiModeNight(const char* str, android::ResTable_config* out = NULL);
+bool parseDensity(const char* str, android::ResTable_config* out = NULL);
+bool parseTouchscreen(const char* str, android::ResTable_config* out = NULL);
+bool parseKeysHidden(const char* str, android::ResTable_config* out = NULL);
+bool parseKeyboard(const char* str, android::ResTable_config* out = NULL);
+bool parseNavHidden(const char* str, android::ResTable_config* out = NULL);
+bool parseNavigation(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenSize(const char* str, android::ResTable_config* out = NULL);
+bool parseVersion(const char* str, android::ResTable_config* out = NULL);
+
+android::String8 getVersion(const android::ResTable_config& config);
+
+/**
+ * Returns true if the two configurations only differ by the specified axis.
+ * The axis mask is a bitmask of CONFIG_* constants.
+ */
+bool isSameExcept(const android::ResTable_config& a, const android::ResTable_config& b, int configMask);
+
+} // namespace AaptConfig
+
+#endif // __AAPT_CONFIG_H
diff --git a/tools/aapt/AaptUtil.cpp b/tools/aapt/AaptUtil.cpp
new file mode 100644
index 0000000..293e144
--- /dev/null
+++ b/tools/aapt/AaptUtil.cpp
@@ -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.
+ */
+
+#include "AaptUtil.h"
+
+using android::Vector;
+using android::String8;
+
+namespace AaptUtil {
+
+Vector<String8> split(const String8& str, const char sep) {
+ Vector<String8> parts;
+ const char* p = str.string();
+ const char* q;
+
+ while (true) {
+ q = strchr(p, sep);
+ if (q == NULL) {
+ parts.add(String8(p, strlen(p)));
+ return parts;
+ }
+
+ parts.add(String8(p, q-p));
+ p = q + 1;
+ }
+ return parts;
+}
+
+Vector<String8> splitAndLowerCase(const String8& str, const char sep) {
+ Vector<String8> parts;
+ const char* p = str.string();
+ const char* q;
+
+ while (true) {
+ q = strchr(p, sep);
+ if (q == NULL) {
+ String8 val(p, strlen(p));
+ val.toLower();
+ parts.add(val);
+ return parts;
+ }
+
+ String8 val(p, q-p);
+ val.toLower();
+ parts.add(val);
+ p = q + 1;
+ }
+ return parts;
+}
+
+} // namespace AaptUtil
diff --git a/tools/aapt/AaptUtil.h b/tools/aapt/AaptUtil.h
new file mode 100644
index 0000000..47a704a
--- /dev/null
+++ b/tools/aapt/AaptUtil.h
@@ -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.
+ */
+
+#ifndef __AAPT_UTIL_H
+#define __AAPT_UTIL_H
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace AaptUtil {
+
+android::Vector<android::String8> split(const android::String8& str, const char sep);
+android::Vector<android::String8> splitAndLowerCase(const android::String8& str, const char sep);
+
+} // namespace AaptUtil
+
+#endif // __AAPT_UTIL_H
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 806f8ff..700afa1 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -1,104 +1,168 @@
-#
-# Copyright 2006 The Android Open Source Project
#
-# Android Asset Packaging Tool
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 tool is prebuilt if we're doing an app-only build.
ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
+# ==========================================================
+# Setup some common variables for the different build
+# targets here.
+# ==========================================================
+LOCAL_PATH:= $(call my-dir)
-aapt_src_files := \
- AaptAssets.cpp \
- Command.cpp \
- CrunchCache.cpp \
- FileFinder.cpp \
- Main.cpp \
- Package.cpp \
- StringPool.cpp \
- XMLNode.cpp \
- ResourceFilter.cpp \
- ResourceIdCache.cpp \
- ResourceTable.cpp \
- Images.cpp \
- Resource.cpp \
+aaptMain := Main.cpp
+aaptSources := \
+ AaptAssets.cpp \
+ AaptConfig.cpp \
+ AaptUtil.cpp \
+ ApkBuilder.cpp \
+ Command.cpp \
+ CrunchCache.cpp \
+ FileFinder.cpp \
+ Package.cpp \
+ StringPool.cpp \
+ XMLNode.cpp \
+ ResourceFilter.cpp \
+ ResourceIdCache.cpp \
+ ResourceTable.cpp \
+ Images.cpp \
+ Resource.cpp \
pseudolocalize.cpp \
SourcePos.cpp \
- WorkQueue.cpp \
+ WorkQueue.cpp \
ZipEntry.cpp \
ZipFile.cpp \
- qsort_r_compat.c
+ qsort_r_compat.c
+
+aaptTests := \
+ tests/AaptConfig_test.cpp \
+ tests/AaptGroupEntry_test.cpp \
+ tests/ResourceFilter_test.cpp
+
+aaptCIncludes := \
+ external/libpng \
+ external/zlib
+
+aaptHostLdLibs :=
+aaptHostStaticLibs := \
+ libandroidfw \
+ libpng \
+ liblog \
+ libutils \
+ libcutils \
+ libexpat \
+ libziparchive-host
-LOCAL_PATH:= $(call my-dir)
+ifeq ($(HOST_OS),linux)
+ aaptHostLdLibs += -lrt -ldl -lpthread
+endif
+
+# Statically link libz for MinGW (Win SDK under Linux),
+# and dynamically link for all others.
+ifneq ($(strip $(USE_MINGW)),)
+ aaptHostStaticLibs += libz
+else
+ aaptHostLdLibs += -lz
+endif
+
+
+# ==========================================================
+# Build the host static library: libaapt
+# ==========================================================
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(aapt_src_files)
+LOCAL_MODULE := libaapt
+
+LOCAL_SRC_FILES := $(aaptSources)
+LOCAL_C_INCLUDES += $(aaptCIncludes)
LOCAL_CFLAGS += -Wno-format-y2k
+LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
ifeq (darwin,$(HOST_OS))
LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS
endif
-LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
+include $(BUILD_HOST_STATIC_LIBRARY)
-LOCAL_C_INCLUDES += external/libpng
-LOCAL_C_INCLUDES += external/zlib
-LOCAL_STATIC_LIBRARIES := \
- libandroidfw \
- libutils \
- libcutils \
- libexpat \
- libpng \
- liblog \
- libziparchive-host
+# ==========================================================
+# Build the host executable: aapt
+# ==========================================================
+include $(CLEAR_VARS)
-ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt -ldl -lpthread
-endif
+LOCAL_MODULE := aapt
-# Statically link libz for MinGW (Win SDK under Linux),
-# and dynamically link for all others.
-ifneq ($(strip $(USE_MINGW)),)
- LOCAL_STATIC_LIBRARIES += libz
-else
- LOCAL_LDLIBS += -lz
-endif
+LOCAL_SRC_FILES := $(aaptMain)
-LOCAL_MODULE := aapt
+LOCAL_STATIC_LIBRARIES += \
+ libaapt \
+ $(aaptHostStaticLibs)
+LOCAL_LDLIBS += $(aaptHostLdLibs)
include $(BUILD_HOST_EXECUTABLE)
-# aapt for running on the device
-# =========================================================
-ifneq ($(SDK_ONLY),true)
+
+# ==========================================================
+# Build the host tests: libaapt_tests
+# ==========================================================
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(aapt_src_files)
+LOCAL_MODULE := libaapt_tests
-LOCAL_MODULE := aapt
+LOCAL_SRC_FILES += $(aaptTests)
+LOCAL_C_INCLUDES += $(LOCAL_PATH)
-LOCAL_C_INCLUDES += bionic
-LOCAL_C_INCLUDES += bionic/libstdc++/include
-LOCAL_C_INCLUDES += external/stlport/stlport
-LOCAL_C_INCLUDES += external/libpng
-LOCAL_C_INCLUDES += external/zlib
+LOCAL_STATIC_LIBRARIES += \
+ libaapt \
+ $(aaptHostStaticLibs)
+LOCAL_LDLIBS += $(aaptHostLdLibs)
-LOCAL_CFLAGS += -Wno-non-virtual-dtor
+include $(BUILD_HOST_NATIVE_TEST)
+
+
+# ==========================================================
+# Build the device executable: aapt
+# ==========================================================
+ifneq ($(SDK_ONLY),true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := aapt
+
+LOCAL_SRC_FILES := $(aaptSources) $(aaptMain)
+LOCAL_C_INCLUDES += \
+ $(aaptCIncludes) \
+ bionic \
+ external/stlport/stlport
LOCAL_SHARED_LIBRARIES := \
- libandroidfw \
- libutils \
- libcutils \
- libpng \
- liblog \
- libz
+ libandroidfw \
+ libutils \
+ libcutils \
+ libpng \
+ liblog \
+ libz
LOCAL_STATIC_LIBRARIES := \
- libstlport_static \
- libexpat_static
+ libstlport_static \
+ libexpat_static
+
+LOCAL_CPPFLAGS += -Wno-non-virtual-dtor
include $(BUILD_EXECUTABLE)
-endif
+
+endif # Not SDK_ONLY
endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
diff --git a/tools/aapt/ApkBuilder.cpp b/tools/aapt/ApkBuilder.cpp
new file mode 100644
index 0000000..12f6040
--- /dev/null
+++ b/tools/aapt/ApkBuilder.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "AaptAssets.h"
+#include "ApkBuilder.h"
+
+using namespace android;
+
+ApkBuilder::ApkBuilder(const sp<WeakResourceFilter>& configFilter)
+ : mConfigFilter(configFilter)
+ , mDefaultFilter(new AndResourceFilter()) {
+ // Add the default split, which is present for all APKs.
+ mDefaultFilter->addFilter(mConfigFilter);
+ mSplits.add(new ApkSplit(std::set<ConfigDescription>(), mDefaultFilter, true));
+}
+
+status_t ApkBuilder::createSplitForConfigs(const std::set<ConfigDescription>& configs) {
+ const size_t N = mSplits.size();
+ for (size_t i = 0; i < N; i++) {
+ const std::set<ConfigDescription>& splitConfigs = mSplits[i]->getConfigs();
+ std::set<ConfigDescription>::const_iterator iter = configs.begin();
+ for (; iter != configs.end(); iter++) {
+ if (splitConfigs.count(*iter) > 0) {
+ // Can't have overlapping configurations.
+ fprintf(stderr, "ERROR: Split configuration '%s' is already defined "
+ "in another split.\n", iter->toString().string());
+ return ALREADY_EXISTS;
+ }
+ }
+ }
+
+ sp<StrongResourceFilter> splitFilter = new StrongResourceFilter(configs);
+
+ // Add the inverse filter of this split filter to the base apk filter so it will
+ // omit resources that belong in this split.
+ mDefaultFilter->addFilter(new InverseResourceFilter(splitFilter));
+
+ // Now add the apk-wide config filter to our split filter.
+ sp<AndResourceFilter> filter = new AndResourceFilter();
+ filter->addFilter(splitFilter);
+ filter->addFilter(mConfigFilter);
+ mSplits.add(new ApkSplit(configs, filter));
+ return NO_ERROR;
+}
+
+status_t ApkBuilder::addEntry(const String8& path, const sp<AaptFile>& file) {
+ const size_t N = mSplits.size();
+ for (size_t i = 0; i < N; i++) {
+ if (mSplits[i]->matches(file)) {
+ return mSplits.editItemAt(i)->addEntry(path, file);
+ }
+ }
+ // Entry can be dropped if it doesn't match any split. This will only happen
+ // if the enry doesn't mConfigFilter.
+ return NO_ERROR;
+}
+
+void ApkBuilder::print() const {
+ fprintf(stderr, "APK Builder\n");
+ fprintf(stderr, "-----------\n");
+ const size_t N = mSplits.size();
+ for (size_t i = 0; i < N; i++) {
+ mSplits[i]->print();
+ fprintf(stderr, "\n");
+ }
+}
+
+ApkSplit::ApkSplit(const std::set<ConfigDescription>& configs, const sp<ResourceFilter>& filter, bool isBase)
+ : mConfigs(configs), mFilter(filter), mIsBase(isBase) {
+ std::set<ConfigDescription>::const_iterator iter = configs.begin();
+ for (; iter != configs.end(); iter++) {
+ if (mName.size() > 0) {
+ mName.append(",");
+ mDirName.append("_");
+ }
+
+ String8 configStr = iter->toString();
+ mName.append(configStr);
+ mDirName.append(configStr);
+ }
+}
+
+status_t ApkSplit::addEntry(const String8& path, const sp<AaptFile>& file) {
+ if (!mFiles.insert(OutputEntry(path, file)).second) {
+ // Duplicate file.
+ return ALREADY_EXISTS;
+ }
+ return NO_ERROR;
+}
+
+void ApkSplit::print() const {
+ fprintf(stderr, "APK Split '%s'\n", mName.string());
+
+ std::set<OutputEntry>::const_iterator iter = mFiles.begin();
+ for (; iter != mFiles.end(); iter++) {
+ fprintf(stderr, " %s (%s)\n", iter->getPath().string(), iter->getFile()->getSourceFile().string());
+ }
+}
diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h
new file mode 100644
index 0000000..a4b7d4a
--- /dev/null
+++ b/tools/aapt/ApkBuilder.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 __APK_BUILDER_H
+#define __APK_BUILDER_H
+
+#include <set>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+#include "ConfigDescription.h"
+#include "OutputSet.h"
+#include "ResourceFilter.h"
+
+class ApkSplit;
+class AaptFile;
+
+class ApkBuilder : public android::RefBase {
+public:
+ ApkBuilder(const sp<WeakResourceFilter>& configFilter);
+
+ /**
+ * Tells the builder to generate a separate APK for resources that
+ * match the configurations specified. Split APKs can not have
+ * overlapping resources.
+ *
+ * NOTE: All splits should be set up before any files are added.
+ */
+ android::status_t createSplitForConfigs(const std::set<ConfigDescription>& configs);
+
+ /**
+ * Adds a file to be written to the final APK. It's name must not collide
+ * with that of any files previously added. When a Split APK is being
+ * generated, duplicates can exist as long as they are in different splits
+ * (resources.arsc, AndroidManifest.xml).
+ */
+ android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file);
+
+ android::Vector<sp<ApkSplit> >& getSplits() {
+ return mSplits;
+ }
+
+ void print() const;
+
+private:
+ android::sp<ResourceFilter> mConfigFilter;
+ android::sp<AndResourceFilter> mDefaultFilter;
+ android::Vector<sp<ApkSplit> > mSplits;
+};
+
+class ApkSplit : public OutputSet {
+public:
+ android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file);
+
+ const std::set<OutputEntry>& getEntries() const {
+ return mFiles;
+ }
+
+ const std::set<ConfigDescription>& getConfigs() const {
+ return mConfigs;
+ }
+
+ bool matches(const sp<AaptFile>& file) const {
+ return mFilter->match(file->getGroupEntry().toParams());
+ }
+
+ sp<ResourceFilter> getResourceFilter() const {
+ return mFilter;
+ }
+
+ const android::String8& getPrintableName() const {
+ return mName;
+ }
+
+ const android::String8& getDirectorySafeName() const {
+ return mDirName;
+ }
+
+ bool isBase() const {
+ return mIsBase;
+ }
+
+ void print() const;
+
+private:
+ friend class ApkBuilder;
+
+ ApkSplit(const std::set<ConfigDescription>& configs, const android::sp<ResourceFilter>& filter, bool isBase=false);
+
+ std::set<ConfigDescription> mConfigs;
+ const sp<ResourceFilter> mFilter;
+ const bool mIsBase;
+ String8 mName;
+ String8 mDirName;
+ std::set<OutputEntry> mFiles;
+};
+
+#endif // __APK_BUILDER_H
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index ebe1bed..ceb52a0 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -151,10 +151,12 @@ public:
void setPublicOutputFile(const char* file) { mPublicOutputFile = file; }
const char* getRClassDir() const { return mRClassDir; }
void setRClassDir(const char* dir) { mRClassDir = dir; }
- const char* getConfigurations() const { return mConfigurations.size() > 0 ? mConfigurations.string() : NULL; }
+ const android::String8& getConfigurations() const { return mConfigurations; }
void addConfigurations(const char* val) { if (mConfigurations.size() > 0) { mConfigurations.append(","); mConfigurations.append(val); } else { mConfigurations = val; } }
- const char* getPreferredConfigurations() const { return mPreferredConfigurations.size() > 0 ? mPreferredConfigurations.string() : NULL; }
- void addPreferredConfigurations(const char* val) { if (mPreferredConfigurations.size() > 0) { mPreferredConfigurations.append(","); mPreferredConfigurations.append(val); } else { mPreferredConfigurations = val; } }
+ const android::String8& getPreferredDensity() const { return mPreferredDensity; }
+ void setPreferredDensity(const char* val) { mPreferredDensity = val; }
+ void addSplitConfigurations(const char* val) { mPartialConfigurations.add(android::String8(val)); }
+ const android::Vector<android::String8>& getSplitConfigurations() const { return mPartialConfigurations; }
const char* getResourceIntermediatesDir() const { return mResourceIntermediatesDir; }
void setResourceIntermediatesDir(const char* dir) { mResourceIntermediatesDir = dir; }
const android::Vector<const char*>& getPackageIncludes() const { return mPackageIncludes; }
@@ -286,7 +288,8 @@ private:
const char* mRClassDir;
const char* mResourceIntermediatesDir;
android::String8 mConfigurations;
- android::String8 mPreferredConfigurations;
+ android::String8 mPreferredDensity;
+ android::Vector<android::String8> mPartialConfigurations;
android::Vector<const char*> mPackageIncludes;
android::Vector<const char*> mJarFiles;
android::Vector<const char*> mNoCompressExtensions;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 0af1ce1..0360200 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -3,6 +3,7 @@
//
// Android Asset Packaging Tool main entry point.
//
+#include "ApkBuilder.h"
#include "Main.h"
#include "Bundle.h"
#include "ResourceFilter.h"
@@ -2034,6 +2035,47 @@ bail:
return (result != NO_ERROR);
}
+static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder) {
+ const size_t numDirs = dir->getDirs().size();
+ for (size_t i = 0; i < numDirs; i++) {
+ status_t err = addResourcesToBuilder(dir->getDirs().valueAt(i), builder);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ }
+
+ const size_t numFiles = dir->getFiles().size();
+ for (size_t i = 0; i < numFiles; i++) {
+ sp<AaptGroup> gp = dir->getFiles().valueAt(i);
+ const size_t numConfigs = gp->getFiles().size();
+ for (size_t j = 0; j < numConfigs; j++) {
+ status_t err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Failed to add %s (%s) to builder.\n",
+ gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
+ return err;
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
+ if (split->isBase()) {
+ return original;
+ }
+
+ String8 ext(original.getPathExtension());
+ if (ext == String8(".apk")) {
+ return String8::format("%s_%s%s",
+ original.getBasePath().string(),
+ split->getDirectorySafeName().string(),
+ ext.string());
+ }
+
+ return String8::format("%s_%s", original.string(),
+ split->getDirectorySafeName().string());
+}
/*
* Package up an asset directory and associated application files.
@@ -2047,17 +2089,18 @@ int doPackage(Bundle* bundle)
int N;
FILE* fp;
String8 dependencyFile;
+ sp<ApkBuilder> builder;
// -c en_XA or/and ar_XB means do pseudolocalization
- ResourceFilter filter;
- err = filter.parse(bundle->getConfigurations());
+ sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
+ err = configFilter->parse(bundle->getConfigurations());
if (err != NO_ERROR) {
goto bail;
}
- if (filter.containsPseudo()) {
+ if (configFilter->containsPseudo()) {
bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
}
- if (filter.containsPseudoBidi()) {
+ if (configFilter->containsPseudoBidi()) {
bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
}
@@ -2105,9 +2148,32 @@ int doPackage(Bundle* bundle)
assets->print(String8());
}
+ // Create the ApkBuilder, which will collect the compiled files
+ // to write to the final APK (or sets of APKs if we are building
+ // a Split APK.
+ builder = new ApkBuilder(configFilter);
+
+ // If we are generating a Split APK, find out which configurations to split on.
+ if (bundle->getSplitConfigurations().size() > 0) {
+ const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
+ const size_t numSplits = splitStrs.size();
+ for (size_t i = 0; i < numSplits; i++) {
+ std::set<ConfigDescription> configs;
+ if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
+ fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
+ goto bail;
+ }
+
+ err = builder->createSplitForConfigs(configs);
+ if (err != NO_ERROR) {
+ goto bail;
+ }
+ }
+ }
+
// If they asked for any fileAs that need to be compiled, do so.
if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
- err = buildResources(bundle, assets);
+ err = buildResources(bundle, assets, builder);
if (err != 0) {
goto bail;
}
@@ -2194,11 +2260,24 @@ int doPackage(Bundle* bundle)
// Write the apk
if (outputAPKFile) {
- err = writeAPK(bundle, assets, String8(outputAPKFile));
+ // Gather all resources and add them to the APK Builder. The builder will then
+ // figure out which Split they belong in.
+ err = addResourcesToBuilder(assets, builder);
if (err != NO_ERROR) {
- fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
goto bail;
}
+
+ const Vector<sp<ApkSplit> >& splits = builder->getSplits();
+ const size_t numSplits = splits.size();
+ for (size_t i = 0; i < numSplits; i++) {
+ const sp<ApkSplit>& split = splits[i];
+ String8 outputPath = buildApkName(String8(outputAPKFile), split);
+ err = writeAPK(bundle, outputPath, split);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
+ goto bail;
+ }
+ }
}
// If we've been asked to generate a dependency file, we need to finish up here.
diff --git a/tools/aapt/ConfigDescription.h b/tools/aapt/ConfigDescription.h
new file mode 100644
index 0000000..779c423
--- /dev/null
+++ b/tools/aapt/ConfigDescription.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 __CONFIG_DESCRIPTION_H
+#define __CONFIG_DESCRIPTION_H
+
+#include <androidfw/ResourceTypes.h>
+
+/**
+ * Subclass of ResTable_config that adds convenient
+ * initialization and comparison methods.
+ */
+struct ConfigDescription : public android::ResTable_config {
+ ConfigDescription() {
+ memset(this, 0, sizeof(*this));
+ size = sizeof(android::ResTable_config);
+ }
+ ConfigDescription(const android::ResTable_config&o) {
+ *static_cast<android::ResTable_config*>(this) = o;
+ size = sizeof(android::ResTable_config);
+ }
+ ConfigDescription(const ConfigDescription&o) {
+ *static_cast<android::ResTable_config*>(this) = o;
+ }
+
+ ConfigDescription& operator=(const android::ResTable_config& o) {
+ *static_cast<android::ResTable_config*>(this) = o;
+ size = sizeof(android::ResTable_config);
+ return *this;
+ }
+ ConfigDescription& operator=(const ConfigDescription& o) {
+ *static_cast<android::ResTable_config*>(this) = o;
+ return *this;
+ }
+
+ inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
+ inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
+ inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
+ inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
+ inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
+ inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
+};
+
+#endif // __CONFIG_DESCRIPTION_H
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 1cf4783..5a60014 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -70,6 +70,7 @@ void usage(void)
" [-F apk-file] [-J R-file-dir] \\\n"
" [--product product1,product2,...] \\\n"
" [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n"
+ " [--split CONFIGS [--split CONFIGS]] \\\n"
" [raw-files-dir [raw-files-dir] ...] \\\n"
" [--output-text-symbols DIR]\n"
"\n"
@@ -166,10 +167,12 @@ void usage(void)
" generate dependency files in the same directories for R.java and resource package\n"
" --auto-add-overlay\n"
" Automatically add resources that are only in overlays.\n"
- " --preferred-configurations\n"
- " Like the -c option for filtering out unneeded configurations, but\n"
- " only expresses a preference. If there is no resource available with\n"
- " the preferred configuration then it will not be stripped.\n"
+ " --preferred-density\n"
+ " Specifies a preference for a particular density. Resources that do not\n"
+ " match this density and have variants that are a closer match are removed.\n"
+ " --split\n"
+ " Builds a separate split APK for the configurations listed. This can\n"
+ " be loaded alongside the base APK at runtime.\n"
" --rename-manifest-package\n"
" Rewrite the manifest so that its package name is the package name\n"
" given here. Relative class names (for example .Foo) will be\n"
@@ -568,15 +571,24 @@ int main(int argc, char* const argv[])
bundle.setGenDependencies(true);
} else if (strcmp(cp, "-utf16") == 0) {
bundle.setWantUTF16(true);
- } else if (strcmp(cp, "-preferred-configurations") == 0) {
+ } else if (strcmp(cp, "-preferred-density") == 0) {
argc--;
argv++;
if (!argc) {
- fprintf(stderr, "ERROR: No argument supplied for '--preferred-configurations' option\n");
+ fprintf(stderr, "ERROR: No argument supplied for '--preferred-density' option\n");
wantUsage = true;
goto bail;
}
- bundle.addPreferredConfigurations(argv[0]);
+ bundle.setPreferredDensity(argv[0]);
+ } else if (strcmp(cp, "-split") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '--split' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.addSplitConfigurations(argv[0]);
} else if (strcmp(cp, "-rename-manifest-package") == 0) {
argc--;
argv++;
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index a6b39ac..34c4496 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -10,8 +10,12 @@
#include <utils/threads.h>
#include <utils/List.h>
#include <utils/Errors.h>
-#include "Bundle.h"
+#include <utils/StrongPointer.h>
+
#include "AaptAssets.h"
+#include "ApkBuilder.h"
+#include "Bundle.h"
+#include "ResourceFilter.h"
#include "ZipFile.h"
@@ -22,6 +26,8 @@
#include <time.h>
#endif /* BENCHMARK */
+class OutputSet;
+
extern int doVersion(Bundle* bundle);
extern int doList(Bundle* bundle);
extern int doDump(Bundle* bundle);
@@ -34,13 +40,13 @@ extern int doSingleCrunch(Bundle* bundle);
extern int calcPercent(long uncompressedLen, long compressedLen);
extern android::status_t writeAPK(Bundle* bundle,
- const sp<AaptAssets>& assets,
- const android::String8& outputFile);
+ const android::String8& outputFile,
+ const android::sp<OutputSet>& outputSet);
extern android::status_t updatePreProcessedCache(Bundle* bundle);
extern android::status_t buildResources(Bundle* bundle,
- const sp<AaptAssets>& assets);
+ const sp<AaptAssets>& assets, sp<ApkBuilder>& builder);
extern android::status_t writeResourceSymbols(Bundle* bundle,
const sp<AaptAssets>& assets, const String8& pkgName, bool includePrivate);
@@ -49,8 +55,6 @@ extern android::status_t writeProguardFile(Bundle* bundle, const sp<AaptAssets>&
extern bool isValidResourceType(const String8& type);
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
-
extern status_t filterResources(Bundle* bundle, const sp<AaptAssets>& assets);
int dumpResources(Bundle* bundle);
diff --git a/tools/aapt/OutputSet.h b/tools/aapt/OutputSet.h
new file mode 100644
index 0000000..ea9ef70
--- /dev/null
+++ b/tools/aapt/OutputSet.h
@@ -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.
+ */
+
+#ifndef __OUTPUT_SET_H
+#define __OUTPUT_SET_H
+
+#include <set>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+
+class AaptFile;
+
+class OutputEntry {
+public:
+ OutputEntry() {}
+ OutputEntry(const android::String8& path, const android::sp<const AaptFile>& file)
+ : mPath(path), mFile(file) {}
+
+ inline const android::sp<const AaptFile>& getFile() const {
+ return mFile;
+ }
+
+ inline const android::String8& getPath() const {
+ return mPath;
+ }
+
+ bool operator<(const OutputEntry& o) const { return getPath() < o.mPath; }
+ bool operator==(const OutputEntry& o) const { return getPath() == o.mPath; }
+
+private:
+ android::String8 mPath;
+ android::sp<const AaptFile> mFile;
+};
+
+class OutputSet : public virtual android::RefBase {
+public:
+ virtual const std::set<OutputEntry>& getEntries() const = 0;
+
+ virtual ~OutputSet() {}
+};
+
+#endif // __OUTPUT_SET_H
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 872d95c..dc16e35 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -5,6 +5,7 @@
//
#include "Main.h"
#include "AaptAssets.h"
+#include "OutputSet.h"
#include "ResourceTable.h"
#include "ResourceFilter.h"
@@ -36,11 +37,8 @@ static const char* kNoCompressExt[] = {
};
/* fwd decls, so I can write this downward */
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
- const AaptGroupEntry& ge, const ResourceFilter* filter);
-bool processFile(Bundle* bundle, ZipFile* zip,
- const sp<AaptGroup>& group, const sp<AaptFile>& file);
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet);
+bool processFile(Bundle* bundle, ZipFile* zip, String8 storageName, const sp<const AaptFile>& file);
bool okayToCompress(Bundle* bundle, const String8& pathName);
ssize_t processJarFiles(Bundle* bundle, ZipFile* zip);
@@ -51,8 +49,7 @@ ssize_t processJarFiles(Bundle* bundle, ZipFile* zip);
* On success, "bundle->numPackages" will be the number of Zip packages
* we created.
*/
-status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets,
- const String8& outputFile)
+status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>& outputSet)
{
#if BENCHMARK
fprintf(stdout, "BENCHMARK: Starting APK Bundling \n");
@@ -112,7 +109,7 @@ status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets,
printf("Writing all files...\n");
}
- count = processAssets(bundle, zip, assets);
+ count = processAssets(bundle, zip, outputSet);
if (count < 0) {
fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n",
outputFile.string());
@@ -218,72 +215,24 @@ bail:
return result;
}
-ssize_t processAssets(Bundle* bundle, ZipFile* zip,
- const sp<AaptAssets>& assets)
-{
- ResourceFilter filter;
- status_t status = filter.parse(bundle->getConfigurations());
- if (status != NO_ERROR) {
- return -1;
- }
-
- ssize_t count = 0;
-
- const size_t N = assets->getGroupEntries().size();
- for (size_t i=0; i<N; i++) {
- const AaptGroupEntry& ge = assets->getGroupEntries()[i];
-
- ssize_t res = processAssets(bundle, zip, assets, ge, &filter);
- if (res < 0) {
- return res;
- }
-
- count += res;
- }
-
- return count;
-}
-
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
- const AaptGroupEntry& ge, const ResourceFilter* filter)
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet)
{
ssize_t count = 0;
-
- const size_t ND = dir->getDirs().size();
- size_t i;
- for (i=0; i<ND; i++) {
- const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
-
- const bool filterable = filter != NULL && subDir->getLeaf().find("mipmap-") != 0;
-
- if (filterable && subDir->getLeaf() != subDir->getPath() && !filter->match(ge.toParams())) {
- continue;
- }
-
- ssize_t res = processAssets(bundle, zip, subDir, ge, filterable ? filter : NULL);
- if (res < 0) {
- return res;
- }
- count += res;
- }
-
- if (filter != NULL && !filter->match(ge.toParams())) {
- return count;
- }
-
- const size_t NF = dir->getFiles().size();
- for (i=0; i<NF; i++) {
- sp<AaptGroup> gp = dir->getFiles().valueAt(i);
- ssize_t fi = gp->getFiles().indexOfKey(ge);
- if (fi >= 0) {
- sp<AaptFile> fl = gp->getFiles().valueAt(fi);
- if (!processFile(bundle, zip, gp, fl)) {
+ const std::set<OutputEntry>& entries = outputSet->getEntries();
+ std::set<OutputEntry>::const_iterator iter = entries.begin();
+ for (; iter != entries.end(); iter++) {
+ const OutputEntry& entry = *iter;
+ if (entry.getFile() == NULL) {
+ fprintf(stderr, "warning: null file being processed.\n");
+ } else {
+ String8 storagePath(entry.getPath());
+ storagePath.convertToResPath();
+ if (!processFile(bundle, zip, storagePath, entry.getFile())) {
return UNKNOWN_ERROR;
}
count++;
}
}
-
return count;
}
@@ -294,12 +243,10 @@ ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
* delete the existing entry before adding the new one.
*/
bool processFile(Bundle* bundle, ZipFile* zip,
- const sp<AaptGroup>& group, const sp<AaptFile>& file)
+ String8 storageName, const sp<const AaptFile>& file)
{
const bool hasData = file->hasData();
- String8 storageName(group->getPath());
- storageName.convertToResPath();
ZipEntry* entry;
bool fromGzip = false;
status_t result;
@@ -403,8 +350,8 @@ bool processFile(Bundle* bundle, ZipFile* zip,
fprintf(stderr, " Unable to add '%s': file already in archive (try '-u'?)\n",
file->getPrintableSource().string());
} else {
- fprintf(stderr, " Unable to add '%s': Zip add failed\n",
- file->getPrintableSource().string());
+ fprintf(stderr, " Unable to add '%s': Zip add failed (%d)\n",
+ file->getPrintableSource().string(), result);
}
return false;
}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 1348be3..e599643 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -179,24 +179,6 @@ bool isValidResourceType(const String8& type)
|| type == "color" || type == "menu" || type == "mipmap";
}
-static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
-{
- sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc"));
- sp<AaptFile> file;
- if (group != NULL) {
- file = group->getFiles().valueFor(AaptGroupEntry());
- if (file != NULL) {
- return file;
- }
- }
-
- if (!makeIfNecessary) {
- return NULL;
- }
- return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(),
- NULL, String8());
-}
-
static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
const sp<AaptGroup>& grp)
{
@@ -359,23 +341,6 @@ static status_t preProcessImages(const Bundle* bundle, const sp<AaptAssets>& ass
return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
}
-status_t postProcessImages(const sp<AaptAssets>& assets,
- ResourceTable* table,
- const sp<ResourceTypeSet>& set)
-{
- ResourceDirIterator it(set, String8("drawable"));
- bool hasErrors = false;
- ssize_t res;
- while ((res=it.next()) == NO_ERROR) {
- res = postProcessImage(assets, table, it.getFile());
- if (res < NO_ERROR) {
- hasErrors = true;
- }
- }
-
- return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
-}
-
static void collect_files(const sp<AaptDir>& dir,
KeyedVector<String8, sp<ResourceTypeSet> >* resources)
{
@@ -906,7 +871,38 @@ status_t updatePreProcessedCache(Bundle* bundle)
return 0;
}
-status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
+status_t generateAndroidManifestForSplit(const String16& package, const sp<ApkSplit>& split,
+ sp<AaptFile>& outFile) {
+ const String8 filename("AndroidManifest.xml");
+ const String16 androidPrefix("android");
+ const String16 androidNSUri("http://schemas.android.com/apk/res/android");
+ sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri);
+
+ // Build the <manifest> tag
+ sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest"));
+
+ // Add the 'package' attribute which is set to the original package name.
+ manifest->addAttribute(String16(), String16("package"), package);
+
+ // Add the 'split' attribute which describes the configurations included.
+ String8 splitName("config_");
+ splitName.append(split->getDirectorySafeName());
+ manifest->addAttribute(String16(), String16("split"), String16(splitName));
+
+ // Build an empty <application> tag (required).
+ sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application"));
+ manifest->addChild(app);
+ root->addChild(manifest);
+
+ status_t err = root->flatten(outFile, true, true);
+ if (err != NO_ERROR) {
+ return err;
+ }
+ outFile->setCompressionMethod(ZipEntry::kCompressDeflated);
+ return NO_ERROR;
+}
+
+status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder)
{
// First, look for a package file to parse. This is required to
// be able to generate the resource information.
@@ -1122,12 +1118,6 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
// --------------------------------------------------------------------
if (table.hasResources()) {
- sp<AaptFile> resFile(getResourceFile(assets));
- if (resFile == NULL) {
- fprintf(stderr, "Error: unable to generate entry for resource data\n");
- return UNKNOWN_ERROR;
- }
-
err = table.assignResourceIds();
if (err < NO_ERROR) {
return err;
@@ -1235,16 +1225,24 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
}
if (drawables != NULL) {
- err = postProcessImages(assets, &table, drawables);
- if (err != NO_ERROR) {
+ ResourceDirIterator it(drawables, String8("drawable"));
+ while ((err=it.next()) == NO_ERROR) {
+ err = postProcessImage(assets, &table, it.getFile());
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (err < NO_ERROR) {
hasErrors = true;
}
+ err = NO_ERROR;
}
if (colors != NULL) {
ResourceDirIterator it(colors, String8("color"));
while ((err=it.next()) == NO_ERROR) {
- err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
@@ -1320,15 +1318,35 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
return err;
}
- resFile = getResourceFile(assets);
- if (resFile == NULL) {
- fprintf(stderr, "Error: unable to generate entry for resource data\n");
- return UNKNOWN_ERROR;
- }
+ Vector<sp<ApkSplit> >& splits = builder->getSplits();
+ const size_t numSplits = splits.size();
+ for (size_t i = 0; i < numSplits; i++) {
+ sp<ApkSplit>& split = splits.editItemAt(i);
+ sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"),
+ AaptGroupEntry(), String8());
+ err = table.flatten(bundle, split->getResourceFilter(), flattenedTable);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Failed to generate resource table for split '%s'\n",
+ split->getPrintableName().string());
+ return err;
+ }
+ split->addEntry(String8("resources.arsc"), flattenedTable);
- err = table.flatten(bundle, resFile);
- if (err < NO_ERROR) {
- return err;
+ if (split->isBase()) {
+ resFile = flattenedTable;
+ finalResTable.add(flattenedTable->getData(), flattenedTable->getSize());
+ } else {
+ sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"),
+ AaptGroupEntry(), String8());
+ err = generateAndroidManifestForSplit(String16(assets->getPackage()), split,
+ generatedManifest);
+ if (err != NO_ERROR) {
+ fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n",
+ split->getPrintableName().string());
+ return err;
+ }
+ split->addEntry(String8("AndroidManifest.xml"), generatedManifest);
+ }
}
if (bundle->getPublicOutputFile()) {
@@ -1344,18 +1362,13 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
table.writePublicDefinitions(String16(assets->getPackage()), fp);
fclose(fp);
}
-
- // Read resources back in,
- finalResTable.add(resFile->getData(), resFile->getSize());
-
-#if 0
- NOISY(
- printf("Generated resources:\n");
- finalResTable.print();
- )
-#endif
+
+ if (finalResTable.getTableCount() == 0 || resFile == NULL) {
+ fprintf(stderr, "No resource table was generated.\n");
+ return UNKNOWN_ERROR;
+ }
}
-
+
// Perform a basic validation of the manifest file. This time we
// parse it with the comments intact, so that we can use them to
// generate java docs... so we are not going to write this one
diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp
index 8ca852e..de8b4fc 100644
--- a/tools/aapt/ResourceFilter.cpp
+++ b/tools/aapt/ResourceFilter.cpp
@@ -1,119 +1,92 @@
//
-// Copyright 2011 The Android Open Source Project
+// Copyright 2014 The Android Open Source Project
//
// Build resource files from raw assets.
//
#include "ResourceFilter.h"
+#include "AaptUtil.h"
+#include "AaptConfig.h"
status_t
-ResourceFilter::parse(const char* arg)
+WeakResourceFilter::parse(const String8& str)
{
- if (arg == NULL) {
- return 0;
- }
-
- const char* p = arg;
- const char* q;
-
- while (true) {
- q = strchr(p, ',');
- if (q == NULL) {
- q = p + strlen(p);
- }
-
- String8 part(p, q-p);
-
+ Vector<String8> configStrs = AaptUtil::split(str, ',');
+ const size_t N = configStrs.size();
+ mConfigs.clear();
+ mConfigMask = 0;
+ mConfigs.resize(N);
+ for (size_t i = 0; i < N; i++) {
+ const String8& part = configStrs[i];
if (part == "en_XA") {
mContainsPseudoAccented = true;
} else if (part == "ar_XB") {
mContainsPseudoBidi = true;
}
- int axis;
- AxisValue value;
- if (!AaptGroupEntry::parseFilterNamePart(part, &axis, &value)) {
- fprintf(stderr, "Invalid configuration: %s\n", arg);
- fprintf(stderr, " ");
- for (int i=0; i<p-arg; i++) {
- fprintf(stderr, " ");
- }
- for (int i=0; i<q-p; i++) {
- fprintf(stderr, "^");
- }
- fprintf(stderr, "\n");
- return 1;
- }
- ssize_t index = mData.indexOfKey(axis);
- if (index < 0) {
- mData.add(axis, SortedVector<AxisValue>());
- }
- SortedVector<AxisValue>& sv = mData.editValueFor(axis);
- sv.add(value);
+ std::pair<ConfigDescription, uint32_t>& entry = mConfigs.editItemAt(i);
- // If it's a locale with a region, script or variant, we should also match an
- // unmodified locale of the same language
- if (axis == AXIS_LOCALE) {
- if (value.localeValue.region[0] || value.localeValue.script[0] ||
- value.localeValue.variant[0]) {
- AxisValue copy;
- memcpy(copy.localeValue.language, value.localeValue.language,
- sizeof(value.localeValue.language));
- sv.add(copy);
- }
+ AaptLocaleValue val;
+ if (val.initFromFilterString(part)) {
+ // For backwards compatibility, we accept configurations that
+ // only specify locale in the standard 'en_US' format.
+ val.writeTo(&entry.first);
+ } else if (!AaptConfig::parse(part, &entry.first)) {
+ fprintf(stderr, "Invalid configuration: %s\n", part.string());
+ return UNKNOWN_ERROR;
}
- p = q;
- if (!*p) break;
- p++;
+
+ entry.second = mDefault.diff(entry.first);
+
+ // Ignore the version
+ entry.second &= ~ResTable_config::CONFIG_VERSION;
+
+ mConfigMask |= entry.second;
}
return NO_ERROR;
}
bool
-ResourceFilter::isEmpty() const
-{
- return mData.size() == 0;
-}
-
-bool
-ResourceFilter::match(int axis, const AxisValue& value) const
+WeakResourceFilter::match(const ResTable_config& config) const
{
- if (value.intValue == 0 && (value.localeValue.language[0] == 0)) {
- // they didn't specify anything so take everything
- return true;
- }
- ssize_t index = mData.indexOfKey(axis);
- if (index < 0) {
- // we didn't request anything on this axis so take everything
+ uint32_t mask = mDefault.diff(config);
+ if ((mConfigMask & mask) == 0) {
+ // The two configurations don't have any common axis.
return true;
}
- const SortedVector<AxisValue>& sv = mData.valueAt(index);
- return sv.indexOf(value) >= 0;
-}
-
-bool
-ResourceFilter::match(int axis, const ResTable_config& config) const
-{
- return match(axis, AaptGroupEntry::getConfigValueForAxis(config, axis));
-}
-bool
-ResourceFilter::match(const ResTable_config& config) const
-{
- for (int i=AXIS_START; i<=AXIS_END; i++) {
- if (!match(i, AaptGroupEntry::getConfigValueForAxis(config, i))) {
- return false;
+ const size_t N = mConfigs.size();
+ for (size_t i = 0; i < N; i++) {
+ const std::pair<ConfigDescription, uint32_t>& entry = mConfigs[i];
+ uint32_t diff = entry.first.diff(config);
+ if ((diff & entry.second) == 0) {
+ return true;
+ } else if ((diff & entry.second) == ResTable_config::CONFIG_LOCALE) {
+ // If the locales differ, but the languages are the same and
+ // the locale we are matching only has a language specified,
+ // we match.
+ if (config.language[0] && memcmp(config.language, entry.first.language, sizeof(config.language)) == 0) {
+ if (config.country[0] == 0) {
+ return true;
+ }
+ }
}
}
- return true;
+ return false;
}
-const SortedVector<AxisValue>* ResourceFilter::configsForAxis(int axis) const
-{
- ssize_t index = mData.indexOfKey(axis);
- if (index < 0) {
- return NULL;
+status_t
+StrongResourceFilter::parse(const String8& str) {
+ Vector<String8> configStrs = AaptUtil::split(str, ',');
+ ConfigDescription config;
+ mConfigs.clear();
+ for (size_t i = 0; i < configStrs.size(); i++) {
+ if (!AaptConfig::parse(configStrs[i], &config)) {
+ fprintf(stderr, "Invalid configuration: %s\n", configStrs[i].string());
+ return UNKNOWN_ERROR;
+ }
+ mConfigs.insert(config);
}
- return &mData.valueAt(index);
+ return NO_ERROR;
}
diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h
index c57770e..f459584 100644
--- a/tools/aapt/ResourceFilter.h
+++ b/tools/aapt/ResourceFilter.h
@@ -7,31 +7,137 @@
#ifndef RESOURCE_FILTER_H
#define RESOURCE_FILTER_H
+#include <androidfw/ResourceTypes.h>
+#include <set>
+#include <utility>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
#include "AaptAssets.h"
+#include "ConfigDescription.h"
+
+class ResourceFilter : public virtual android::RefBase {
+public:
+ virtual bool match(const android::ResTable_config& config) const = 0;
+};
/**
* Implements logic for parsing and handling "-c" and "--preferred-configurations"
* options.
*/
-class ResourceFilter
-{
+class WeakResourceFilter : public ResourceFilter {
public:
- ResourceFilter() : mData(), mContainsPseudoAccented(false),
- mContainsPseudoBidi(false) {}
- status_t parse(const char* arg);
- bool isEmpty() const;
- bool match(int axis, const ResTable_config& config) const;
- bool match(const ResTable_config& config) const;
- const SortedVector<AxisValue>* configsForAxis(int axis) const;
- inline bool containsPseudo() const { return mContainsPseudoAccented; }
- inline bool containsPseudoBidi() const { return mContainsPseudoBidi; }
+ WeakResourceFilter()
+ : mContainsPseudoAccented(false)
+ , mContainsPseudoBidi(false) {}
+
+ android::status_t parse(const android::String8& str);
+
+ bool match(const android::ResTable_config& config) const;
+
+ inline bool isEmpty() const {
+ return mConfigMask == 0;
+ }
+
+ inline bool containsPseudo() const {
+ return mContainsPseudoAccented;
+ }
+
+ inline bool containsPseudoBidi() const {
+ return mContainsPseudoBidi;
+ }
private:
- bool match(int axis, const AxisValue& value) const;
+ ConfigDescription mDefault;
+ uint32_t mConfigMask;
+ android::Vector<std::pair<ConfigDescription, uint32_t> > mConfigs;
- KeyedVector<int,SortedVector<AxisValue> > mData;
bool mContainsPseudoAccented;
bool mContainsPseudoBidi;
};
+/**
+ * Matches resources that have at least one of the configurations
+ * that this filter is looking for. In order to match a configuration,
+ * the resource must have the exact same configuration.
+ *
+ * This filter acts as a logical OR when matching resources.
+ *
+ * For example, if the filter is looking for resources with
+ * fr-land, de-land, or sw600dp:
+ *
+ * (PASS) fr-land
+ * (FAIL) fr
+ * (PASS) de-land
+ * (FAIL) de
+ * (FAIL) de-sw600dp
+ * (PASS) sw600dp
+ * (FAIL) sw600dp-land
+ */
+class StrongResourceFilter : public ResourceFilter {
+public:
+ StrongResourceFilter() {}
+ StrongResourceFilter(const std::set<ConfigDescription>& configs)
+ : mConfigs(configs) {}
+
+ android::status_t parse(const android::String8& str);
+
+ bool match(const android::ResTable_config& config) const {
+ std::set<ConfigDescription>::const_iterator iter = mConfigs.begin();
+ for (; iter != mConfigs.end(); iter++) {
+ if (iter->compare(config) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ inline const std::set<ConfigDescription>& getConfigs() const {
+ return mConfigs;
+ }
+
+private:
+ std::set<ConfigDescription> mConfigs;
+};
+
+/**
+ * Negates the response of the target filter.
+ */
+class InverseResourceFilter : public ResourceFilter {
+public:
+ InverseResourceFilter(const android::sp<ResourceFilter>& filter)
+ : mFilter(filter) {}
+
+ bool match(const android::ResTable_config& config) const {
+ return !mFilter->match(config);
+ }
+
+private:
+ const android::sp<ResourceFilter> mFilter;
+};
+
+/**
+ * A logical AND of all the added filters.
+ */
+class AndResourceFilter : public ResourceFilter {
+public:
+ void addFilter(const android::sp<ResourceFilter>& filter) {
+ mFilters.add(filter);
+ }
+
+ bool match(const android::ResTable_config& config) const {
+ const size_t N = mFilters.size();
+ for (size_t i = 0; i < N; i++) {
+ if (!mFilters[i]->match(config)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+private:
+ android::Vector<android::sp<ResourceFilter> > mFilters;
+};
#endif
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 6eab65b..efbba40 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2095,10 +2095,10 @@ bool ResourceTable::hasResources() const {
return mNumLocal > 0;
}
-sp<AaptFile> ResourceTable::flatten(Bundle* bundle)
+sp<AaptFile> ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter)
{
sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
- status_t err = flatten(bundle, data);
+ status_t err = flatten(bundle, filter, data);
return err == NO_ERROR ? data : NULL;
}
@@ -2658,8 +2658,8 @@ ResourceTable::validateLocalizations(void)
}
// Check that all requested localizations are present for this string
- if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
- const char* allConfigs = mBundle->getConfigurations();
+ if (mBundle->getConfigurations().size() > 0 && mBundle->getRequireLocalization()) {
+ const char* allConfigs = mBundle->getConfigurations().string();
const char* start = allConfigs;
const char* comma;
@@ -2713,14 +2713,8 @@ ResourceTable::validateLocalizations(void)
return err;
}
-status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
+status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest)
{
- ResourceFilter filter;
- status_t err = filter.parse(bundle->getConfigurations());
- if (err != NO_ERROR) {
- return err;
- }
-
const ConfigDescription nullConfig;
const size_t N = mOrderedPackages.size();
@@ -2795,7 +2789,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
const size_t N = c->getEntries().size();
for (size_t ei=0; ei<N; ei++) {
ConfigDescription config = c->getEntries().keyAt(ei);
- if (filterable && !filter.match(config)) {
+ if (filterable && !filter->match(config)) {
continue;
}
sp<Entry> e = c->getEntries().valueAt(ei);
@@ -2887,7 +2881,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
return amt;
}
- err = flattenLibraryTable(data, libraryPackages);
+ status_t err = flattenLibraryTable(data, libraryPackages);
if (err != NO_ERROR) {
fprintf(stderr, "ERROR: failed to write library table\n");
return err;
@@ -2943,11 +2937,11 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
}
const size_t CN = cl->getEntries().size();
for (size_t ci=0; ci<CN; ci++) {
- if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
+ if (filterable && !filter->match(cl->getEntries().keyAt(ci))) {
continue;
}
for (size_t cj=ci+1; cj<CN; cj++) {
- if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
+ if (filterable && !filter->match(cl->getEntries().keyAt(cj))) {
continue;
}
typeSpecFlags[ei] |= htodl(
@@ -2989,7 +2983,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
config.screenHeightDp,
config.layoutDirection));
- if (filterable && !filter.match(config)) {
+ if (filterable && !filter->match(config)) {
continue;
}
@@ -3108,7 +3102,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
}
ssize_t strStart = dest->getSize();
- err = valueStrings.writeStringBlock(dest);
+ status_t err = valueStrings.writeStringBlock(dest);
if (err != NO_ERROR) {
return err;
}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index ec8fd17..a73993c 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -7,8 +7,10 @@
#ifndef RESOURCE_TABLE_H
#define RESOURCE_TABLE_H
+#include "ConfigDescription.h"
#include "StringPool.h"
#include "SourcePos.h"
+#include "ResourceFilter.h"
#include <set>
#include <map>
@@ -76,37 +78,6 @@ public:
class Type;
class Entry;
- struct ConfigDescription : public ResTable_config {
- ConfigDescription() {
- memset(this, 0, sizeof(*this));
- size = sizeof(ResTable_config);
- }
- ConfigDescription(const ResTable_config&o) {
- *static_cast<ResTable_config*>(this) = o;
- size = sizeof(ResTable_config);
- }
- ConfigDescription(const ConfigDescription&o) {
- *static_cast<ResTable_config*>(this) = o;
- }
-
- ConfigDescription& operator=(const ResTable_config& o) {
- *static_cast<ResTable_config*>(this) = o;
- size = sizeof(ResTable_config);
- return *this;
- }
- ConfigDescription& operator=(const ConfigDescription& o) {
- *static_cast<ResTable_config*>(this) = o;
- return *this;
- }
-
- inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
- inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
- inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
- inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
- inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
- inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
- };
-
ResourceTable(Bundle* bundle, const String16& assetsPackage);
status_t addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets);
@@ -182,7 +153,7 @@ public:
size_t numLocalResources() const;
bool hasResources() const;
- sp<AaptFile> flatten(Bundle*);
+ sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter);
static inline uint32_t makeResId(uint32_t packageId,
uint32_t typeId,
@@ -223,7 +194,7 @@ public:
void addLocalization(const String16& name, const String8& locale, const SourcePos& src);
status_t validateLocalizations(void);
- status_t flatten(Bundle*, const sp<AaptFile>& dest);
+ status_t flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest);
status_t flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs);
void writePublicDefinitions(const String16& package, FILE* fp);
diff --git a/tools/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp
new file mode 100644
index 0000000..e795d81
--- /dev/null
+++ b/tools/aapt/tests/AaptConfig_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <utils/String8.h>
+#include <gtest/gtest.h>
+
+#include "AaptConfig.h"
+#include "ConfigDescription.h"
+#include "TestHelper.h"
+
+using android::String8;
+
+static ::testing::AssertionResult TestParse(const String8& input, ConfigDescription* config=NULL) {
+ if (AaptConfig::parse(String8(input), config)) {
+ return ::testing::AssertionSuccess() << input << " was successfully parsed";
+ }
+ return ::testing::AssertionFailure() << input << " could not be parsed";
+}
+
+static ::testing::AssertionResult TestParse(const char* input, ConfigDescription* config=NULL) {
+ return TestParse(String8(input), config);
+}
+
+TEST(AaptConfigTest, ParseFailWhenQualifiersAreOutOfOrder) {
+ EXPECT_FALSE(TestParse("en-sw600dp-ldrtl"));
+ EXPECT_FALSE(TestParse("land-en"));
+ EXPECT_FALSE(TestParse("hdpi-320dpi"));
+}
+
+TEST(AaptConfigTest, ParseFailWhenQualifiersAreNotMatched) {
+ EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL"));
+}
+
+TEST(AaptConfigTest, ParseFailWhenQualifiersHaveTrailingDash) {
+ EXPECT_FALSE(TestParse("en-sw600dp-land-"));
+}
+
+TEST(AaptConfigTest, ParseBasicQualifiers) {
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("", &config));
+ EXPECT_EQ(String8(""), config.toString());
+
+ EXPECT_TRUE(TestParse("fr-land", &config));
+ EXPECT_EQ(String8("fr-land"), config.toString());
+
+ EXPECT_TRUE(TestParse("mcc310-pl-sw720dp-normal-long-port-night-"
+ "xhdpi-keyssoft-qwerty-navexposed-nonav", &config));
+ EXPECT_EQ(String8("mcc310-pl-sw720dp-normal-long-port-night-"
+ "xhdpi-keyssoft-qwerty-navexposed-nonav-v13"), config.toString());
+}
+
+TEST(AaptConfigTest, ParseLocales) {
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("en-rUS", &config));
+ EXPECT_EQ(String8("en-US"), config.toString());
+}
+
+TEST(AaptConfigTest, ParseQualifierAddedInApi13) {
+ ConfigDescription config;
+ EXPECT_TRUE(TestParse("sw600dp", &config));
+ EXPECT_EQ(String8("sw600dp-v13"), config.toString());
+
+ EXPECT_TRUE(TestParse("sw600dp-v8", &config));
+ EXPECT_EQ(String8("sw600dp-v13"), config.toString());
+}
diff --git a/tools/aapt/tests/AaptGroupEntry_test.cpp b/tools/aapt/tests/AaptGroupEntry_test.cpp
new file mode 100644
index 0000000..7348a08
--- /dev/null
+++ b/tools/aapt/tests/AaptGroupEntry_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <utils/String8.h>
+#include <gtest/gtest.h>
+
+#include "AaptAssets.h"
+#include "ResourceFilter.h"
+#include "TestHelper.h"
+
+using android::String8;
+
+static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const String8& dirName,
+ String8* outType) {
+ if (entry.initFromDirName(dirName, outType)) {
+ return ::testing::AssertionSuccess() << dirName << " was successfully parsed";
+ }
+ return ::testing::AssertionFailure() << dirName << " could not be parsed";
+}
+
+static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const char* input,
+ String8* outType) {
+ return TestParse(entry, String8(input), outType);
+}
+
+TEST(AaptGroupEntryTest, ParseNoQualifier) {
+ AaptGroupEntry entry;
+ String8 type;
+ EXPECT_TRUE(TestParse(entry, "menu", &type));
+ EXPECT_EQ(String8("menu"), type);
+}
+
+TEST(AaptGroupEntryTest, ParseCorrectType) {
+ AaptGroupEntry entry;
+ String8 type;
+ EXPECT_TRUE(TestParse(entry, "anim", &type));
+ EXPECT_EQ(String8("anim"), type);
+
+ EXPECT_TRUE(TestParse(entry, "animator", &type));
+ EXPECT_EQ(String8("animator"), type);
+}
diff --git a/tools/aapt/tests/ResourceFilter_test.cpp b/tools/aapt/tests/ResourceFilter_test.cpp
new file mode 100644
index 0000000..30697bb
--- /dev/null
+++ b/tools/aapt/tests/ResourceFilter_test.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+#include <gtest/gtest.h>
+
+#include "AaptConfig.h"
+#include "ResourceFilter.h"
+#include "ConfigDescription.h"
+
+using android::String8;
+
+// In this context, 'Axis' represents a particular field in the configuration,
+// such as language or density.
+
+TEST(WeakResourceFilterTest, EmptyFilterMatchesAnything) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("")));
+
+ ConfigDescription config;
+ config.density = 320;
+
+ EXPECT_TRUE(filter.match(config));
+
+ config.language[0] = 'f';
+ config.language[1] = 'r';
+
+ EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithUnrelatedAxis) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
+
+ ConfigDescription config;
+ config.density = 320;
+
+ EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxis) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
+
+ ConfigDescription config;
+ config.language[0] = 'f';
+ config.language[1] = 'r';
+
+ EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxisAndOtherUnrelatedAxis) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
+
+ ConfigDescription config;
+ config.language[0] = 'f';
+ config.language[1] = 'r';
+ config.density = 320;
+
+ EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, DoesNotMatchConfigWithDifferentValueAxis) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
+
+ ConfigDescription config;
+ config.language[0] = 'd';
+ config.language[1] = 'e';
+
+ EXPECT_FALSE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithSameLanguageButNoRegionSpecified) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("de-rDE")));
+
+ ConfigDescription config;
+ config.language[0] = 'd';
+ config.language[1] = 'e';
+
+ EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, ParsesStandardLocaleOnlyString) {
+ WeakResourceFilter filter;
+ EXPECT_EQ(NO_ERROR, filter.parse(String8("de_DE")));
+}
+
+TEST(WeakResourceFilterTest, IgnoresVersion) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("normal-v4")));
+
+ ConfigDescription config;
+ config.smallestScreenWidthDp = 600;
+ config.version = 13;
+
+ // The configs don't match on any axis besides version, which should be ignored.
+ EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithRegion) {
+ WeakResourceFilter filter;
+ ASSERT_EQ(NO_ERROR, filter.parse(String8("kok,kok_IN,kok_419")));
+
+ ConfigDescription config;
+ AaptLocaleValue val;
+ ASSERT_TRUE(val.initFromFilterString(String8("kok_IN")));
+ val.writeTo(&config);
+
+ EXPECT_TRUE(filter.match(config));
+}
+
diff --git a/tools/aapt/tests/TestHelper.h b/tools/aapt/tests/TestHelper.h
new file mode 100644
index 0000000..7917483
--- /dev/null
+++ b/tools/aapt/tests/TestHelper.h
@@ -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.
+ */
+
+#ifndef __TEST_HELPER_H
+#define __TEST_HELPER_H
+
+#include <utils/String8.h>
+
+namespace android {
+
+/**
+ * Stream operator for nicely printing String8's in gtest output.
+ */
+inline std::ostream& operator<<(std::ostream& stream, const String8& str) {
+ return stream << str.string();
+}
+
+}
+
+#endif
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index ce8c8b8..85b81d9 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -506,6 +506,12 @@ public class WifiConfiguration implements Parcelable {
* @hide
*/
public boolean isValid() {
+ if (SSID == null)
+ return false;
+
+ if (allowedKeyManagement == null)
+ return false;
+
if (allowedKeyManagement.cardinality() > 1) {
if (allowedKeyManagement.cardinality() != 2) {
return false;