summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt18
-rw-r--r--api/removed.txt9
-rw-r--r--api/system-current.txt24
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java15
-rw-r--r--cmds/dpm/src/com/android/commands/dpm/Dpm.java37
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java2
-rw-r--r--core/java/android/animation/AnimatorSet.java107
-rw-r--r--core/java/android/animation/ValueAnimator.java33
-rw-r--r--core/java/android/app/Activity.java10
-rw-r--r--core/java/android/app/ActivityManager.java35
-rw-r--r--core/java/android/app/ActivityManagerInternal.java9
-rw-r--r--core/java/android/app/ActivityManagerNative.java31
-rw-r--r--core/java/android/app/ActivityThread.java5
-rw-r--r--core/java/android/app/AutomaticZenRule.java48
-rw-r--r--core/java/android/app/IActivityManager.java8
-rw-r--r--core/java/android/app/INotificationManager.aidl8
-rw-r--r--core/java/android/app/NotificationManager.java40
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java36
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java309
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl13
-rw-r--r--core/java/android/content/pm/PackageInstaller.java22
-rw-r--r--core/java/android/content/pm/PackageManager.java9
-rw-r--r--core/java/android/content/pm/PackageParser.java15
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java2
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java12
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java12
-rw-r--r--core/java/android/net/DhcpResults.java9
-rw-r--r--core/java/android/net/LinkProperties.java3
-rw-r--r--core/java/android/os/BaseBundle.java12
-rw-r--r--core/java/android/os/Build.java8
-rw-r--r--core/java/android/os/ParcelableParcel.java10
-rw-r--r--core/java/android/preference/PreferenceFragment.java3
-rw-r--r--core/java/android/preference/PreferenceGroup.java9
-rw-r--r--core/java/android/provider/CallLog.java16
-rw-r--r--core/java/android/provider/DocumentsContract.java29
-rw-r--r--core/java/android/service/notification/ConditionProviderService.java26
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java38
-rw-r--r--core/java/android/util/MathUtils.java41
-rw-r--r--core/java/android/view/IWindow.aidl14
-rw-r--r--core/java/android/view/SurfaceView.java3
-rw-r--r--core/java/android/view/ThreadedRenderer.java10
-rw-r--r--core/java/android/view/ViewRootImpl.java197
-rw-r--r--core/java/android/view/Window.java13
-rw-r--r--core/java/android/view/WindowCallbacks.java55
-rw-r--r--core/java/android/view/WindowManagerInternal.java3
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java3
-rw-r--r--core/java/android/webkit/WebView.java24
-rw-r--r--core/java/android/webkit/WebViewProvider.java3
-rw-r--r--core/java/android/widget/AdapterViewAnimator.java9
-rw-r--r--core/java/android/widget/FastScroller.java1
-rw-r--r--core/java/android/widget/RadialTimePickerView.java431
-rw-r--r--core/java/android/widget/TimePickerClockDelegate.java4
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java273
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java8
-rw-r--r--core/java/com/android/internal/policy/PhoneWindow.java18
-rw-r--r--core/java/com/android/internal/view/BaseIWindow.java8
-rw-r--r--core/java/com/android/internal/widget/NonClientDecorView.java339
-rw-r--r--core/java/com/android/internal/widget/ResolverDrawerLayout.java111
-rw-r--r--core/jni/android/graphics/Paint.cpp356
-rw-r--r--core/jni/android_graphics_Canvas.cpp102
-rw-r--r--core/jni/android_view_DisplayEventReceiver.cpp2
-rw-r--r--core/jni/android_view_DisplayListCanvas.cpp24
-rw-r--r--core/jni/android_view_ThreadedRenderer.cpp7
-rw-r--r--core/res/res/layout/chooser_grid.xml2
-rw-r--r--core/res/res/layout/list_content.xml3
-rw-r--r--core/res/res/layout/preference_list_fragment.xml1
-rw-r--r--core/res/res/layout/resolve_grid_item.xml3
-rw-r--r--core/res/res/values-fa/strings.xml16
-rw-r--r--core/res/res/values-it/strings.xml2
-rw-r--r--core/res/res/values-sv/strings.xml68
-rw-r--r--core/res/res/values-sw/strings.xml2
-rw-r--r--core/res/res/values-vi/strings.xml12
-rw-r--r--core/res/res/values/colors_holo.xml4
-rw-r--r--core/res/res/values/colors_legacy.xml26
-rw-r--r--core/res/res/values/styles_material.xml3
-rw-r--r--core/tests/coretests/src/android/net/LinkPropertiesTest.java26
-rw-r--r--core/tests/coretests/src/android/util/OrientationUtil.java67
-rw-r--r--core/tests/coretests/src/android/widget/TextViewActivityTest.java27
-rw-r--r--data/keyboards/Vendor_18d1_Product_5018.kcm321
-rw-r--r--data/keyboards/Vendor_18d1_Product_5018.kl84
-rw-r--r--docs/html/guide/topics/resources/providing-resources.jd9
-rw-r--r--graphics/java/android/graphics/Paint.java481
-rw-r--r--graphics/tests/graphicstests/src/android/graphics/PaintTest.java56
-rw-r--r--libs/hwui/Android.mk1
-rw-r--r--libs/hwui/CanvasState.cpp60
-rw-r--r--libs/hwui/CanvasState.h24
-rw-r--r--libs/hwui/ClipArea.cpp20
-rw-r--r--libs/hwui/ClipArea.h2
-rw-r--r--libs/hwui/DeferredDisplayList.cpp24
-rw-r--r--libs/hwui/DeferredDisplayList.h10
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp3
-rw-r--r--libs/hwui/DisplayListOp.h22
-rw-r--r--libs/hwui/FontRenderer.cpp8
-rw-r--r--libs/hwui/FontRenderer.h2
-rw-r--r--libs/hwui/Glop.h4
-rw-r--r--libs/hwui/GlopBuilder.cpp3
-rw-r--r--libs/hwui/GlopBuilder.h5
-rw-r--r--libs/hwui/Layer.cpp3
-rw-r--r--libs/hwui/LayerRenderer.cpp2
-rw-r--r--libs/hwui/OpenGLRenderer.cpp71
-rwxr-xr-xlibs/hwui/OpenGLRenderer.h61
-rw-r--r--libs/hwui/Rect.h47
-rw-r--r--libs/hwui/RenderNode.cpp9
-rw-r--r--libs/hwui/RenderProperties.cpp7
-rw-r--r--libs/hwui/RenderProperties.h2
-rw-r--r--libs/hwui/ShadowTessellator.cpp4
-rw-r--r--libs/hwui/Snapshot.cpp6
-rw-r--r--libs/hwui/Snapshot.h6
-rw-r--r--libs/hwui/SpotShadow.cpp2
-rw-r--r--libs/hwui/font/Font.cpp2
-rw-r--r--libs/hwui/renderstate/RenderState.cpp4
-rw-r--r--libs/hwui/renderstate/RenderState.h2
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp63
-rw-r--r--libs/hwui/renderthread/CanvasContext.h6
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp9
-rw-r--r--libs/hwui/renderthread/RenderProxy.h2
-rw-r--r--libs/hwui/tests/Benchmark.h54
-rw-r--r--libs/hwui/tests/TestContext.cpp23
-rw-r--r--libs/hwui/tests/TestContext.h2
-rw-r--r--libs/hwui/tests/TreeContentAnimation.cpp385
-rw-r--r--libs/hwui/tests/how_to_run.txt14
-rw-r--r--libs/hwui/tests/main.cpp476
-rw-r--r--libs/hwui/utils/PaintUtils.h40
-rw-r--r--libs/hwui/utils/TinyHashMap.h70
-rw-r--r--media/java/android/media/MediaActionSound.java2
-rw-r--r--media/java/android/media/MediaPlayer.java5
-rw-r--r--media/java/android/media/MediaRecorder.java26
-rw-r--r--media/java/android/media/Metadata.java10
-rw-r--r--media/jni/android_media_MediaRecorder.cpp18
-rw-r--r--packages/DocumentsUI/AndroidManifest.xml2
-rw-r--r--packages/DocumentsUI/res/color/item_doc_grid_overlay.xml6
-rw-r--r--packages/DocumentsUI/res/color/item_doc_list_background_activated.xml4
-rw-r--r--packages/DocumentsUI/res/menu/activity.xml18
-rw-r--r--packages/DocumentsUI/res/values-af/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-am/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-ar/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-az-rAZ/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-bg/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-bn-rBD/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-ca/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-cs/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-da/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-de/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-el/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-en-rAU/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-en-rGB/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-en-rIN/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-es-rUS/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-es/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-et-rEE/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-eu-rES/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-fa/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-fi/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-fr-rCA/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-fr/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-gl-rES/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-gu-rIN/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-hi/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-hr/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-hu/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-hy-rAM/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-in/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-is-rIS/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-it/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-iw/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-ja/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-ka-rGE/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-kk-rKZ/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-km-rKH/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-kn-rIN/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-ko/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-ky-rKG/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-lo-rLA/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-lt/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-lv/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-mk-rMK/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-ml-rIN/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-mn-rMN/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-mr-rIN/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-ms-rMY/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-my-rMM/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-nb/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-ne-rNP/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-nl/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-pa-rIN/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-pl/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-pt-rBR/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-pt-rPT/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-pt/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-ro/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-ru/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-si-rLK/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-sk/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-sl/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-sq-rAL/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-sr/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-sv/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-sw/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-sw720dp/styles.xml2
-rw-r--r--packages/DocumentsUI/res/values-ta-rIN/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-te-rIN/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-th/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-tl/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-tr/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-uk/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-ur-rPK/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values-uz-rUZ/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-vi/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-zh-rCN/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-zh-rHK/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-zh-rTW/strings.xml6
-rw-r--r--packages/DocumentsUI/res/values-zu/strings.xml3
-rw-r--r--packages/DocumentsUI/res/values/config.xml2
-rw-r--r--packages/DocumentsUI/res/values/strings.xml2
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java131
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/CopyService.java23
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java2
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java86
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java46
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java2
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java78
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java5
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java47
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java3
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/Menus.java50
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/Shared.java20
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/Snackbars.java37
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java2
-rw-r--r--packages/SystemUI/proguard.flags8
-rw-r--r--packages/SystemUI/res/drawable/qs_customizer_background.xml19
-rw-r--r--packages/SystemUI/res/layout/qs_customize_panel.xml11
-rw-r--r--packages/SystemUI/res/values-hy-rAM/strings.xml4
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml2
-rw-r--r--packages/SystemUI/res/values/config.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeService.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooter.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/TileLayout.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/Recents.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java134
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java844
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/events/ui/ResizeTaskEvent.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/DragView.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java165
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java149
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java134
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java162
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java102
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java46
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java5
-rw-r--r--packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java75
-rw-r--r--rs/java/android/renderscript/RenderScript.java18
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AutoclickController.java8
-rw-r--r--services/accessibility/java/com/android/server/accessibility/TouchExplorer.java156
-rw-r--r--services/core/java/com/android/server/GestureLauncherService.java9
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java35
-rw-r--r--services/core/java/com/android/server/MountService.java123
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java150
-rwxr-xr-xservices/core/java/com/android/server/am/ActivityRecord.java8
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java45
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java111
-rw-r--r--services/core/java/com/android/server/am/CompatModePackages.java8
-rw-r--r--services/core/java/com/android/server/am/TaskPersister.java12
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java4
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java60
-rw-r--r--services/core/java/com/android/server/audio/RotationHelper.java211
-rw-r--r--services/core/java/com/android/server/job/JobSchedulerService.java23
-rw-r--r--services/core/java/com/android/server/job/JobServiceContext.java2
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java34
-rw-r--r--services/core/java/com/android/server/notification/ZenModeConditions.java4
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java153
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java20
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java129
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java166
-rw-r--r--services/core/java/com/android/server/pm/UserRestrictionsUtils.java122
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java66
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java30
-rw-r--r--services/core/java/com/android/server/wm/DimBehindController.java299
-rw-r--r--services/core/java/com/android/server/wm/DimLayer.java10
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java71
-rw-r--r--services/core/java/com/android/server/wm/DockedStackDividerController.java230
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java27
-rw-r--r--services/core/java/com/android/server/wm/Task.java179
-rw-r--r--services/core/java/com/android/server/wm/TaskPositioner.java52
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java99
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java22
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java259
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java61
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java51
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java7
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java458
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/Owners.java65
-rw-r--r--services/net/java/android/net/dhcp/DhcpClient.java26
-rw-r--r--services/net/java/android/net/ip/IpReachabilityMonitor.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/MountServiceTests.java45
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java64
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java415
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java94
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java11
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java73
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java2
-rw-r--r--telecomm/java/android/telecom/PhoneAccount.java37
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java27
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java2
-rw-r--r--tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java6
-rw-r--r--tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java29
-rw-r--r--tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java59
-rw-r--r--tools/layoutlib/bridge/src/android/content/res/BridgeResources.java8
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java1
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java8
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java210
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java79
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java73
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java8
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java3
356 files changed, 9793 insertions, 4728 deletions
diff --git a/api/current.txt b/api/current.txt
index e3c93663e189..ef18bf9f05f6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4021,6 +4021,8 @@ package android.app {
ctor public AutomaticZenRule(android.os.Parcel);
method public int describeContents();
method public android.net.Uri getConditionId();
+ method public long getCreationTime();
+ method public java.lang.String getId();
method public int getInterruptionFilter();
method public java.lang.String getName();
method public android.content.ComponentName getOwner();
@@ -5078,7 +5080,7 @@ package android.app {
}
public class NotificationManager {
- method public boolean addOrUpdateAutomaticZenRule(android.app.AutomaticZenRule);
+ method public android.app.AutomaticZenRule addAutomaticZenRule(android.app.AutomaticZenRule);
method public void cancel(int);
method public void cancel(java.lang.String, int);
method public void cancelAll();
@@ -5091,9 +5093,9 @@ package android.app {
method public void notify(int, android.app.Notification);
method public void notify(java.lang.String, int, android.app.Notification);
method public boolean removeAutomaticZenRule(java.lang.String);
- method public boolean renameAutomaticZenRule(java.lang.String, java.lang.String);
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
+ method public boolean updateAutomaticZenRule(android.app.AutomaticZenRule);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
@@ -5629,7 +5631,7 @@ package android.app.admin {
method public void onPasswordFailed(android.content.Context, android.content.Intent);
method public void onPasswordSucceeded(android.content.Context, android.content.Intent);
method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent);
- method public void onReadyForUserInitialization(android.content.Context, android.content.Intent);
+ method public deprecated void onReadyForUserInitialization(android.content.Context, android.content.Intent);
method public void onReceive(android.content.Context, android.content.Intent);
method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long);
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
@@ -9235,6 +9237,7 @@ package android.content.pm {
method public void setAppLabel(java.lang.CharSequence);
method public void setAppPackageName(java.lang.String);
method public void setInstallLocation(int);
+ method public void setOriginatingUid(int);
method public void setOriginatingUri(android.net.Uri);
method public void setReferrerUri(android.net.Uri);
method public void setSize(long);
@@ -22847,6 +22850,7 @@ package android.os {
field public static final int LOLLIPOP = 21; // 0x15
field public static final int LOLLIPOP_MR1 = 22; // 0x16
field public static final int M = 23; // 0x17
+ field public static final int N = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -24961,6 +24965,7 @@ package android.provider {
public static class CallLog.Calls implements android.provider.BaseColumns {
ctor public CallLog.Calls();
method public static java.lang.String getLastOutgoingCall(android.content.Context);
+ field public static final int BLOCKED_TYPE = 6; // 0x6
field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number";
field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri";
field public static final java.lang.String CACHED_MATCHED_NUMBER = "matched_number";
@@ -24999,6 +25004,7 @@ package android.provider {
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
+ field public static final int REJECTED_TYPE = 5; // 0x5
field public static final java.lang.String TRANSCRIPTION = "transcription";
field public static final java.lang.String TYPE = "type";
field public static final int VOICEMAIL_TYPE = 4; // 0x4
@@ -28845,10 +28851,8 @@ package android.service.notification {
method public abstract void onRequestConditions(int);
method public abstract void onSubscribe(android.net.Uri);
method public abstract void onUnsubscribe(android.net.Uri);
- field public static final java.lang.String EXTRA_CONDITION_ID = "android.content.automatic.conditionId";
- field public static final java.lang.String EXTRA_RULE_NAME = "android.content.automatic.ruleName";
+ field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
- field public static final java.lang.String META_DATA_DEFAULT_CONDITION_ID = "android.service.zen.automatic.defaultConditionId";
field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
@@ -30474,6 +30478,7 @@ package android.telecom {
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public android.net.Uri getAddress();
method public int getCapabilities();
+ method public android.os.Bundle getExtras();
method public int getHighlightColor();
method public android.graphics.drawable.Icon getIcon();
method public java.lang.CharSequence getLabel();
@@ -30506,6 +30511,7 @@ package android.telecom {
method public android.telecom.PhoneAccount build();
method public android.telecom.PhoneAccount.Builder setAddress(android.net.Uri);
method public android.telecom.PhoneAccount.Builder setCapabilities(int);
+ method public android.telecom.PhoneAccount.Builder setExtras(android.os.Bundle);
method public android.telecom.PhoneAccount.Builder setHighlightColor(int);
method public android.telecom.PhoneAccount.Builder setIcon(android.graphics.drawable.Icon);
method public android.telecom.PhoneAccount.Builder setShortDescription(java.lang.CharSequence);
diff --git a/api/removed.txt b/api/removed.txt
index 642d2a8ac19c..f12e61eea332 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -6,6 +6,15 @@ package android.app {
}
+package android.app.admin {
+
+ public class DevicePolicyManager {
+ method public deprecated java.lang.String getDeviceInitializerApp();
+ method public deprecated android.content.ComponentName getDeviceInitializerComponent();
+ }
+
+}
+
package android.content.pm {
public class PackageInfo implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index b1590d85c766..0b216bdddb30 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4132,6 +4132,8 @@ package android.app {
ctor public AutomaticZenRule(android.os.Parcel);
method public int describeContents();
method public android.net.Uri getConditionId();
+ method public long getCreationTime();
+ method public java.lang.String getId();
method public int getInterruptionFilter();
method public java.lang.String getName();
method public android.content.ComponentName getOwner();
@@ -5195,7 +5197,7 @@ package android.app {
}
public class NotificationManager {
- method public boolean addOrUpdateAutomaticZenRule(android.app.AutomaticZenRule);
+ method public android.app.AutomaticZenRule addAutomaticZenRule(android.app.AutomaticZenRule);
method public void cancel(int);
method public void cancel(java.lang.String, int);
method public void cancelAll();
@@ -5208,9 +5210,9 @@ package android.app {
method public void notify(int, android.app.Notification);
method public void notify(java.lang.String, int, android.app.Notification);
method public boolean removeAutomaticZenRule(java.lang.String);
- method public boolean renameAutomaticZenRule(java.lang.String, java.lang.String);
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
+ method public boolean updateAutomaticZenRule(android.app.AutomaticZenRule);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED";
@@ -5750,7 +5752,7 @@ package android.app.admin {
method public void onPasswordFailed(android.content.Context, android.content.Intent);
method public void onPasswordSucceeded(android.content.Context, android.content.Intent);
method public void onProfileProvisioningComplete(android.content.Context, android.content.Intent);
- method public void onReadyForUserInitialization(android.content.Context, android.content.Intent);
+ method public deprecated void onReadyForUserInitialization(android.content.Context, android.content.Intent);
method public void onReceive(android.content.Context, android.content.Intent);
method public void onSystemUpdatePending(android.content.Context, android.content.Intent, long);
field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
@@ -5792,8 +5794,8 @@ package android.app.admin {
method public boolean getCrossProfileCallerIdDisabled(android.content.ComponentName);
method public java.util.List<java.lang.String> getCrossProfileWidgetProviders(android.content.ComponentName);
method public int getCurrentFailedPasswordAttempts();
- method public java.lang.String getDeviceInitializerApp();
- method public android.content.ComponentName getDeviceInitializerComponent();
+ method public deprecated java.lang.String getDeviceInitializerApp();
+ method public deprecated android.content.ComponentName getDeviceInitializerComponent();
method public java.lang.String getDeviceOwner();
method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName);
method public int getKeyguardDisabledFeatures(android.content.ComponentName);
@@ -9518,6 +9520,7 @@ package android.content.pm {
method public void setAppPackageName(java.lang.String);
method public void setGrantedRuntimePermissions(java.lang.String[]);
method public void setInstallLocation(int);
+ method public void setOriginatingUid(int);
method public void setOriginatingUri(android.net.Uri);
method public void setReferrerUri(android.net.Uri);
method public void setSize(long);
@@ -24791,6 +24794,7 @@ package android.os {
field public static final int LOLLIPOP = 21; // 0x15
field public static final int LOLLIPOP_MR1 = 22; // 0x16
field public static final int M = 23; // 0x17
+ field public static final int N = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -26917,6 +26921,7 @@ package android.provider {
public static class CallLog.Calls implements android.provider.BaseColumns {
ctor public CallLog.Calls();
method public static java.lang.String getLastOutgoingCall(android.content.Context);
+ field public static final int BLOCKED_TYPE = 6; // 0x6
field public static final java.lang.String CACHED_FORMATTED_NUMBER = "formatted_number";
field public static final java.lang.String CACHED_LOOKUP_URI = "lookup_uri";
field public static final java.lang.String CACHED_MATCHED_NUMBER = "matched_number";
@@ -26955,6 +26960,7 @@ package android.provider {
field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
+ field public static final int REJECTED_TYPE = 5; // 0x5
field public static final java.lang.String TRANSCRIPTION = "transcription";
field public static final java.lang.String TYPE = "type";
field public static final int VOICEMAIL_TYPE = 4; // 0x4
@@ -30934,10 +30940,8 @@ package android.service.notification {
method public abstract void onRequestConditions(int);
method public abstract void onSubscribe(android.net.Uri);
method public abstract void onUnsubscribe(android.net.Uri);
- field public static final java.lang.String EXTRA_CONDITION_ID = "android.content.automatic.conditionId";
- field public static final java.lang.String EXTRA_RULE_NAME = "android.content.automatic.ruleName";
+ field public static final java.lang.String EXTRA_RULE_ID = "android.content.automatic.ruleId";
field public static final java.lang.String META_DATA_CONFIGURATION_ACTIVITY = "android.service.zen.automatic.configurationActivity";
- field public static final java.lang.String META_DATA_DEFAULT_CONDITION_ID = "android.service.zen.automatic.defaultConditionId";
field public static final java.lang.String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
@@ -32673,6 +32677,7 @@ package android.telecom {
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public android.net.Uri getAddress();
method public int getCapabilities();
+ method public android.os.Bundle getExtras();
method public int getHighlightColor();
method public android.graphics.drawable.Icon getIcon();
method public java.lang.CharSequence getLabel();
@@ -32706,6 +32711,7 @@ package android.telecom {
method public android.telecom.PhoneAccount build();
method public android.telecom.PhoneAccount.Builder setAddress(android.net.Uri);
method public android.telecom.PhoneAccount.Builder setCapabilities(int);
+ method public android.telecom.PhoneAccount.Builder setExtras(android.os.Bundle);
method public android.telecom.PhoneAccount.Builder setHighlightColor(int);
method public android.telecom.PhoneAccount.Builder setIcon(android.graphics.drawable.Icon);
method public android.telecom.PhoneAccount.Builder setShortDescription(java.lang.CharSequence);
@@ -41876,6 +41882,7 @@ package android.webkit {
method public void super_scrollTo(int, int);
method public boolean super_setFrame(int, int, int, int);
method public void super_setLayoutParams(android.view.ViewGroup.LayoutParams);
+ method public void super_startActivityForResult(android.content.Intent, int);
}
public static abstract class WebView.VisualStateCallback {
@@ -42110,6 +42117,7 @@ package android.webkit {
public static abstract interface WebViewProvider.ViewDelegate {
method public abstract boolean dispatchKeyEvent(android.view.KeyEvent);
method public abstract android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider();
+ method public abstract void onActivityResult(int, int, android.content.Intent);
method public abstract void onAttachedToWindow();
method public abstract void onConfigurationChanged(android.content.res.Configuration);
method public abstract android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.EditorInfo);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 5c9fd51d606d..4a13136fa8d9 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -44,6 +44,7 @@ import android.content.Intent;
import android.content.pm.IPackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.net.Uri;
@@ -63,6 +64,7 @@ import android.util.ArrayMap;
import android.view.IWindowManager;
import com.android.internal.os.BaseCommand;
+import com.android.internal.util.Preconditions;
import java.io.BufferedReader;
import java.io.File;
@@ -168,6 +170,7 @@ public class Am extends BaseCommand {
" am get-inactive [--user <USER_ID>] <PACKAGE>\n" +
" am send-trim-memory [--user <USER_ID>] <PROCESS>\n" +
" [HIDDEN|RUNNING_MODERATE|BACKGROUND|RUNNING_LOW|MODERATE|RUNNING_CRITICAL|COMPLETE]\n" +
+ " am get-current-user\n" +
"\n" +
"am start: start an Activity. Options are:\n" +
" -D: enable debugging\n" +
@@ -334,7 +337,9 @@ public class Am extends BaseCommand {
"\n" +
"am get-inactive: returns the inactive state of an app.\n" +
"\n" +
- "am send-trim-memory: Send a memory trim event to a <PROCESS>.\n" +
+ "am send-trim-memory: send a memory trim event to a <PROCESS>.\n" +
+ "\n" +
+ "am get-current-user: returns id of the current foreground user.\n" +
"\n" +
"<INTENT> specifications include these flags and arguments:\n" +
" [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
@@ -464,6 +469,8 @@ public class Am extends BaseCommand {
runGetInactive();
} else if (op.equals("send-trim-memory")) {
runSendTrimMemory();
+ } else if (op.equals("get-current-user")) {
+ runGetCurrentUser();
} else {
showError("Error: unknown command '" + op + "'");
}
@@ -2712,6 +2719,12 @@ public class Am extends BaseCommand {
}
}
+ private void runGetCurrentUser() throws Exception {
+ UserInfo currentUser = Preconditions.checkNotNull(mAm.getCurrentUser(),
+ "Current user not set");
+ System.out.println(currentUser.id);
+ }
+
/**
* Open the given file for sending into the system process. This verifies
* with SELinux that the system will have access to the file.
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 214dc5d35a24..ea530094e691 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -44,6 +44,7 @@ public final class Dpm extends BaseCommand {
private IDevicePolicyManager mDevicePolicyManager;
private int mUserId = UserHandle.USER_SYSTEM;
+ private String mName = "";
private ComponentName mComponent = null;
@Override
@@ -52,8 +53,8 @@ public final class Dpm extends BaseCommand {
"usage: dpm [subcommand] [options]\n" +
"usage: dpm set-active-admin [ --user <USER_ID> ] <COMPONENT>\n" +
// STOPSHIP Finalize it
- "usage: dpm set-device-owner [ --user <USER_ID> *EXPERIMENTAL* ] <COMPONENT>\n" +
- "usage: dpm set-profile-owner [ --user <USER_ID> ] <COMPONENT>\n" +
+ "usage: dpm set-device-owner [ --user <USER_ID> *EXPERIMENTAL* ] [ --name <NAME> ] <COMPONENT>\n" +
+ "usage: dpm set-profile-owner [ --user <USER_ID> ] [ --name <NAME> ] <COMPONENT>\n" +
"\n" +
"dpm set-active-admin: Sets the given component as active admin" +
" for an existing user.\n" +
@@ -90,47 +91,51 @@ public final class Dpm extends BaseCommand {
}
}
- private void parseArgs(boolean canHaveUser) {
- String nextArg = nextArgRequired();
- if (canHaveUser && "--user".equals(nextArg)) {
- mUserId = parseInt(nextArgRequired());
- nextArg = nextArgRequired();
+ private void parseArgs(boolean canHaveUser, boolean canHaveName) {
+ String opt;
+ while ((opt = nextOption()) != null) {
+ if (canHaveUser && "--user".equals(opt)) {
+ mUserId = parseInt(nextArgRequired());
+ } else if (canHaveName && "--name".equals(opt)) {
+ mName = nextArgRequired();
+ } else {
+ throw new IllegalArgumentException("Unknown option: " + opt);
+ }
}
- mComponent = parseComponentName(nextArg);
+ mComponent = parseComponentName(nextArgRequired());
}
private void runSetActiveAdmin() throws RemoteException {
- parseArgs(true);
+ parseArgs(/*canHaveUser=*/ true, /*canHaveName=*/ false);
mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
System.out.println("Success: Active admin set to component " + mComponent.toShortString());
}
private void runSetDeviceOwner() throws RemoteException {
- parseArgs(true);
+ parseArgs(/*canHaveUser=*/ true, /*canHaveName=*/ true);
mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
- String packageName = mComponent.getPackageName();
try {
- if (!mDevicePolicyManager.setDeviceOwner(packageName, null /*ownerName*/, mUserId)) {
+ if (!mDevicePolicyManager.setDeviceOwner(mComponent, mName, mUserId)) {
throw new RuntimeException(
- "Can't set package " + packageName + " as device owner.");
+ "Can't set package " + mComponent + " as device owner.");
}
} catch (Exception e) {
// Need to remove the admin that we just added.
mDevicePolicyManager.removeActiveAdmin(mComponent, UserHandle.USER_SYSTEM);
throw e;
}
- System.out.println("Success: Device owner set to package " + packageName);
+ System.out.println("Success: Device owner set to package " + mComponent);
System.out.println("Active admin set to component " + mComponent.toShortString());
}
private void runSetProfileOwner() throws RemoteException {
- parseArgs(true);
+ parseArgs(/*canHaveUser=*/ true, /*canHaveName=*/ true);
mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
try {
- if (!mDevicePolicyManager.setProfileOwner(mComponent, "" /*ownerName*/, mUserId)) {
+ if (!mDevicePolicyManager.setProfileOwner(mComponent, mName, mUserId)) {
throw new RuntimeException("Can't set component " + mComponent.toShortString() +
" as profile owner for user " + mUserId);
}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index ebf508514b3c..393956f29948 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -624,7 +624,7 @@ public final class Pm {
}
}
- String grp = nextOption();
+ String grp = nextArg();
ArrayList<String> groupList = new ArrayList<String>();
if (groups) {
List<PermissionGroupInfo> infos =
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 16f825da6f31..d444638c715a 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -603,7 +603,17 @@ public final class AnimatorSet extends Animator {
createDependencyGraph();
// Now that all dependencies are set up, start the animations that should be started.
- start(mRootNode);
+ boolean setIsEmpty = false;
+ if (mStartDelay > 0) {
+ start(mRootNode);
+ } else if (mNodes.size() > 1) {
+ // No delay, but there are other animators in the set
+ onChildAnimatorEnded(mDelayAnim);
+ } else {
+ // Set is empty, no delay, no other animation. Skip to end in this case
+ setIsEmpty = true;
+ }
+
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
@@ -612,18 +622,9 @@ public final class AnimatorSet extends Animator {
tmpListeners.get(i).onAnimationStart(this);
}
}
- if (mNodes.size() == 0 && mStartDelay == 0) {
- // Handle unusual case where empty AnimatorSet is started - should send out
- // end event immediately since the event will not be sent out at all otherwise
- mStarted = false;
- if (mListeners != null) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onAnimationEnd(this);
- }
- }
+ if (setIsEmpty) {
+ // In the case of empty AnimatorSet, we will trigger the onAnimationEnd() right away.
+ onChildAnimatorEnded(mDelayAnim);
}
}
@@ -751,44 +752,7 @@ public final class AnimatorSet extends Animator {
public void onAnimationEnd(Animator animation) {
animation.removeListener(this);
mAnimatorSet.mPlayingSet.remove(animation);
- Node animNode = mAnimatorSet.mNodeMap.get(animation);
- animNode.mEnded = true;
-
- if (!mAnimatorSet.mTerminated) {
- List<Node> children = animNode.mChildNodes;
- // Start children animations, if any.
- int childrenSize = children == null ? 0 : children.size();
- for (int i = 0; i < childrenSize; i++) {
- if (children.get(i).mLatestParent == animNode) {
- mAnimatorSet.start(children.get(i));
- }
- }
- // Listeners are already notified of the AnimatorSet ending in cancel() or
- // end(); the logic below only kicks in when animations end normally
- boolean allDone = true;
- // Traverse the tree and find if there's any unfinished node
- int size = mAnimatorSet.mNodes.size();
- for (int i = 0; i < size; i++) {
- if (!mAnimatorSet.mNodes.get(i).mEnded) {
- allDone = false;
- break;
- }
- }
- if (allDone) {
- // If this was the last child animation to end, then notify listeners that this
- // AnimatorSet has ended
- if (mAnimatorSet.mListeners != null) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mAnimatorSet.mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
- }
- }
- mAnimatorSet.mStarted = false;
- mAnimatorSet.mPaused = false;
- }
- }
+ mAnimatorSet.onChildAnimatorEnded(animation);
}
// Nothing to do
@@ -801,6 +765,47 @@ public final class AnimatorSet extends Animator {
}
+ private void onChildAnimatorEnded(Animator animation) {
+ Node animNode = mNodeMap.get(animation);
+ animNode.mEnded = true;
+
+ if (!mTerminated) {
+ List<Node> children = animNode.mChildNodes;
+ // Start children animations, if any.
+ int childrenSize = children == null ? 0 : children.size();
+ for (int i = 0; i < childrenSize; i++) {
+ if (children.get(i).mLatestParent == animNode) {
+ start(children.get(i));
+ }
+ }
+ // Listeners are already notified of the AnimatorSet ending in cancel() or
+ // end(); the logic below only kicks in when animations end normally
+ boolean allDone = true;
+ // Traverse the tree and find if there's any unfinished node
+ int size = mNodes.size();
+ for (int i = 0; i < size; i++) {
+ if (!mNodes.get(i).mEnded) {
+ allDone = false;
+ break;
+ }
+ }
+ if (allDone) {
+ // If this was the last child animation to end, then notify listeners that this
+ // AnimatorSet has ended
+ if (mListeners != null) {
+ ArrayList<AnimatorListener> tmpListeners =
+ (ArrayList<AnimatorListener>) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationEnd(this);
+ }
+ }
+ mStarted = false;
+ mPaused = false;
+ }
+ }
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index da9a8c8d8900..9b905bdffc0a 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -625,14 +625,19 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
/**
* Gets the current position of the animation in time, which is equal to the current
* time minus the time that the animation started. An animation that is not yet started will
- * return a value of zero.
+ * return a value of zero, unless the animation has has its play time set via
+ * {@link #setCurrentPlayTime(long)} or {@link #setCurrentFraction(float)}, in which case
+ * it will return the time that was set.
*
* @return The current position in time of the animation.
*/
public long getCurrentPlayTime() {
- if (!mInitialized || !mStarted) {
+ if (!mInitialized || (!mStarted && mSeekFraction < 0)) {
return 0;
}
+ if (mSeekFraction >= 0) {
+ return (long) (mUnscaledDuration * mSeekFraction);
+ }
return AnimationUtils.currentAnimationTimeMillis() - mStartTime;
}
@@ -669,6 +674,10 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
* The frame delay may be ignored when the animation system uses an external timing
* source, such as the display refresh rate (vsync), to govern animations.
*
+ * Note that this method should be called from the same thread that {@link #start()} is
+ * called in order to check the frame delay for that animation. A runtime exception will be
+ * thrown if the calling thread does not have a Looper.
+ *
* @return the requested time between frames, in milliseconds
*/
public static long getFrameDelay() {
@@ -685,6 +694,10 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
* The frame delay may be ignored when the animation system uses an external timing
* source, such as the display refresh rate (vsync), to govern animations.
*
+ * Note that this method should be called from the same thread that {@link #start()} is
+ * called in order to have the new frame delay take effect on that animation. A runtime
+ * exception will be thrown if the calling thread does not have a Looper.
+ *
* @param frameDelay the requested time between frames, in milliseconds
*/
public static void setFrameDelay(long frameDelay) {
@@ -924,6 +937,13 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
updateScaledDuration(); // in case the scale factor has changed since creation time
AnimationHandler animationHandler = AnimationHandler.getInstance();
animationHandler.addAnimationFrameCallback(this, mStartDelay);
+
+ if (mStartDelay == 0) {
+ // If there's no start delay, init the animation and notify start listeners right away
+ // Otherwise, postpone this until the first frame after the start delay.
+ startAnimation();
+ setCurrentFraction(mSeekFraction == -1 ? 0 : mSeekFraction);
+ }
}
@Override
@@ -1067,6 +1087,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
mStartListenersCalled = false;
mPlayingBackwards = false;
mReversing = false;
+ mLastFrameTime = 0;
mCurrentIteration = 0;
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
@@ -1176,12 +1197,13 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
* @hide
*/
public final void doAnimationFrame(long frameTime) {
- mLastFrameTime = frameTime;
AnimationHandler handler = AnimationHandler.getInstance();
- if (!mRunning) {
+ if (mLastFrameTime == 0) {
// First frame
handler.addOneShotCommitCallback(this);
- startAnimation();
+ if (mStartDelay > 0) {
+ startAnimation();
+ }
if (mSeekFraction < 0) {
mStartTime = frameTime;
} else {
@@ -1191,6 +1213,7 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
}
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
+ mLastFrameTime = frameTime;
if (mPaused) {
if (mPauseTime < 0) {
mPauseTime = frameTime;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f60250cc6eb1..d47c0aa3e58b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -687,9 +687,15 @@ public class Activity extends ContextThemeWrapper
/** @hide Task isn't finished when activity is finished */
public static final int DONT_FINISH_TASK_WITH_ACTIVITY = 0;
- /** @hide Task is finished if the finishing activity is the root of the task */
+ /**
+ * @hide Task is finished if the finishing activity is the root of the task. To preserve the
+ * past behavior the task is also removed from recents.
+ */
public static final int FINISH_TASK_WITH_ROOT_ACTIVITY = 1;
- /** @hide Task is finished along with the finishing activity*/
+ /**
+ * @hide Task is finished along with the finishing activity, but it is not removed from
+ * recents.
+ */
public static final int FINISH_TASK_WITH_ACTIVITY = 2;
static final String FRAGMENTS_TAG = "android:fragments";
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 6606f9be2e79..61a9a84a5965 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -468,36 +468,51 @@ public class ActivityManager {
*/
public static final int DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT = 1;
-
/**
* Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
- * that the resize is from the window manager (instead of the user).
+ * that the resize doesn't need to preserve the window, and can be skipped if bounds
+ * is unchanged. This mode is used by window manager in most cases.
* @hide
*/
public static final int RESIZE_MODE_SYSTEM = 0;
/**
* Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
- * that the resize is from the window manager (instead of the user) due to a screen
- * rotation change.
+ * that the resize should preserve the window if possible.
* @hide
*/
- public static final int RESIZE_MODE_SYSTEM_SCREEN_ROTATION = 1;
+ public static final int RESIZE_MODE_PRESERVE_WINDOW = (0x1 << 0);
/**
* Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
- * that the resize is initiated by the user (most likely via a drag action on the
- * window's edge or corner).
+ * that the resize should be performed even if the bounds appears unchanged.
+ * @hide
+ */
+ public static final int RESIZE_MODE_FORCED = (0x1 << 1);
+
+ /**
+ * Input parameter to {@link android.app.IActivityManager#resizeTask} used by window
+ * manager during a screen rotation.
* @hide
*/
- public static final int RESIZE_MODE_USER = 2;
+ public static final int RESIZE_MODE_SYSTEM_SCREEN_ROTATION = RESIZE_MODE_PRESERVE_WINDOW;
+
+ /**
+ * Input parameter to {@link android.app.IActivityManager#resizeTask} used when the
+ * resize is due to a drag action.
+ * @hide
+ */
+ public static final int RESIZE_MODE_USER = RESIZE_MODE_PRESERVE_WINDOW;
/**
* Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates
- * that the resize should be performed even if the bounds appears unchanged.
+ * that the resize should preserve the window if possible, and should not be skipped
+ * even if the bounds is unchanged. Usually used to force a resizing when a drag action
+ * is ending.
* @hide
*/
- public static final int RESIZE_MODE_FORCED = 3;
+ public static final int RESIZE_MODE_USER_FORCED =
+ RESIZE_MODE_PRESERVE_WINDOW | RESIZE_MODE_FORCED;
/** @hide */
public int getFrontActivityScreenCompatMode() {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 40eb799f26b9..373a23fa7422 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -55,4 +55,13 @@ public abstract class ActivityManagerInternal {
* @param userId ID of the user or {@link android.os.UserHandle#USER_ALL}
*/
public abstract ComponentName getHomeActivityForUser(int userId);
+
+ /**
+ * Called when a user has been deleted. This can happen during normal device usage
+ * or just at startup, when partially removed users are purged. Any state persisted by the
+ * ActivityManager should be purged now.
+ *
+ * @param userId The user being cleaned up.
+ */
+ public abstract void onUserRemoved(int userId);
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index cb1a89fd6601..faa3a43f6c2e 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -330,9 +330,11 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case START_ACTIVITY_FROM_RECENTS_TRANSACTION:
{
data.enforceInterface(IActivityManager.descriptor);
- int taskId = data.readInt();
- Bundle options = data.readInt() == 0 ? null : Bundle.CREATOR.createFromParcel(data);
- int result = startActivityFromRecents(taskId, options);
+ final int taskId = data.readInt();
+ final int launchStackId = data.readInt();
+ final Bundle options =
+ data.readInt() == 0 ? null : Bundle.CREATOR.createFromParcel(data);
+ final int result = startActivityFromRecents(taskId, launchStackId, options);
reply.writeNoException();
reply.writeInt(result);
return true;
@@ -2689,6 +2691,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
reply.writeNoException();
return true;
}
+ case REMOVE_STACK: {
+ data.enforceInterface(IActivityManager.descriptor);
+ final int stackId = data.readInt();
+ removeStack(stackId);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -2984,11 +2993,13 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
return result != 0;
}
- public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException {
+ public int startActivityFromRecents(int taskId, int launchStackId, Bundle options)
+ throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(taskId);
+ data.writeInt(launchStackId);
if (options == null) {
data.writeInt(0);
} else {
@@ -6235,5 +6246,17 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
+ @Override
+ public void removeStack(int stackId) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(stackId);
+ mRemote.transact(REMOVE_STACK, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4b8efab3e59e..9f24de8dfe65 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3956,13 +3956,12 @@ public final class ActivityThread {
}
IBinder wtoken = v.getWindowToken();
if (r.activity.mWindowAdded) {
- boolean reuseForResize = r.window.hasNonClientDecorView() && r.mPreserveWindow;
- if (r.onlyLocalRequest || reuseForResize) {
+ if (r.onlyLocalRequest || r.mPreserveWindow) {
// Hold off on removing this until the new activity's
// window is being added.
r.mPendingRemoveWindow = r.window;
r.mPendingRemoveWindowManager = wm;
- if (reuseForResize) {
+ if (r.mPreserveWindow) {
// We can only keep the part of the view hierarchy that we control,
// everything else must be removed, because it might not be able to
// behave properly when activity is relaunching.
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index fea5624411ad..e5fa02b248f4 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -33,6 +33,8 @@ public class AutomaticZenRule implements Parcelable {
private int interruptionFilter;
private Uri conditionId;
private ComponentName owner;
+ private String id;
+ private long creationTime;
/**
* Creates an automatic zen rule.
@@ -55,6 +57,17 @@ public class AutomaticZenRule implements Parcelable {
this.enabled = enabled;
}
+ /**
+ * @SystemApi
+ * @hide
+ */
+ public AutomaticZenRule(String name, ComponentName owner, Uri conditionId,
+ int interruptionFilter, boolean enabled, String id, long creationTime) {
+ this(name, owner, conditionId, interruptionFilter, enabled);
+ this.id = id;
+ this.creationTime = creationTime;
+ }
+
public AutomaticZenRule(Parcel source) {
enabled = source.readInt() == 1;
if (source.readInt() == 1) {
@@ -63,6 +76,10 @@ public class AutomaticZenRule implements Parcelable {
interruptionFilter = source.readInt();
conditionId = source.readParcelable(null);
owner = source.readParcelable(null);
+ if (source.readInt() == 1) {
+ id = source.readString();
+ }
+ creationTime = source.readLong();
}
/**
@@ -101,6 +118,20 @@ public class AutomaticZenRule implements Parcelable {
}
/**
+ * Returns the wall time in milliseconds when this rule was created, if known.
+ */
+ public long getCreationTime() {
+ return creationTime;
+ }
+
+ /**
+ * Returns the unique identifier for this rule.
+ */
+ public String getId() {
+ return id;
+ }
+
+ /**
* Sets the representation of the state that causes this rule to become active.
*/
public void setConditionId(Uri conditionId) {
@@ -146,6 +177,13 @@ public class AutomaticZenRule implements Parcelable {
dest.writeInt(interruptionFilter);
dest.writeParcelable(conditionId, 0);
dest.writeParcelable(owner, 0);
+ if (id != null) {
+ dest.writeInt(1);
+ dest.writeString(id);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeLong(creationTime);
}
@Override
@@ -156,6 +194,8 @@ public class AutomaticZenRule implements Parcelable {
.append(",interruptionFilter=").append(interruptionFilter)
.append(",conditionId=").append(conditionId)
.append(",owner=").append(owner)
+ .append(",id=").append(id)
+ .append(",creationTime=").append(creationTime)
.append(']').toString();
}
@@ -168,12 +208,14 @@ public class AutomaticZenRule implements Parcelable {
&& Objects.equals(other.name, name)
&& other.interruptionFilter == interruptionFilter
&& Objects.equals(other.conditionId, conditionId)
- && Objects.equals(other.owner, owner);
+ && Objects.equals(other.owner, owner)
+ && Objects.equals(other.id, id)
+ && other.creationTime == creationTime;
}
@Override
public int hashCode() {
- return Objects.hash(enabled, name, interruptionFilter, conditionId, owner);
+ return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, id, creationTime);
}
public static final Parcelable.Creator<AutomaticZenRule> CREATOR
@@ -187,4 +229,4 @@ public class AutomaticZenRule implements Parcelable {
return new AutomaticZenRule[size];
}
};
-} \ No newline at end of file
+}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 478fdd14e093..e23620d9b0ea 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -16,8 +16,8 @@
package android.app;
-import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.RunningServiceInfo;
+import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.StackInfo;
import android.app.assist.AssistContent;
import android.app.assist.AssistStructure;
@@ -92,7 +92,8 @@ public interface IActivityManager extends IInterface {
int userId) throws RemoteException;
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent, Bundle options) throws RemoteException;
- public int startActivityFromRecents(int taskId, Bundle options) throws RemoteException;
+ public int startActivityFromRecents(int taskId, int launchStackId, Bundle options)
+ throws RemoteException;
public boolean finishActivity(IBinder token, int code, Intent data, int finishTask)
throws RemoteException;
public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
@@ -537,6 +538,8 @@ public interface IActivityManager extends IInterface {
public void suppressResizeConfigChanges(boolean suppress) throws RemoteException;
+ public void removeStack(int stackId) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -894,4 +897,5 @@ public interface IActivityManager extends IInterface {
int REPORT_SIZE_CONFIGURATIONS = IBinder.FIRST_CALL_TRANSACTION + 345;
int MOVE_TASK_TO_DOCKED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 346;
int SUPPRESS_RESIZE_CONFIG_CHANGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 347;
+ int REMOVE_STACK = IBinder.FIRST_CALL_TRANSACTION + 348;
}
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 920fbe9cfc1c..e95a35a23b6c 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -93,11 +93,11 @@ interface INotificationManager
String[] getPackagesRequestingNotificationPolicyAccess();
boolean isNotificationPolicyAccessGrantedForPackage(String pkg);
void setNotificationPolicyAccessGranted(String pkg, boolean granted);
- AutomaticZenRule getAutomaticZenRule(String name);
+ AutomaticZenRule getAutomaticZenRule(String id);
List<AutomaticZenRule> getAutomaticZenRules();
- boolean addOrUpdateAutomaticZenRule(in AutomaticZenRule automaticZenRule);
- boolean renameAutomaticZenRule(String oldName, String newName);
- boolean removeAutomaticZenRule(String name);
+ AutomaticZenRule addAutomaticZenRule(in AutomaticZenRule automaticZenRule);
+ boolean updateAutomaticZenRule(in AutomaticZenRule automaticZenRule);
+ boolean removeAutomaticZenRule(String id);
byte[] getBackupPayload(int user);
void applyRestore(in byte[] payload, int user);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index cbf198bb9415..cb0ff33ee9b3 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -436,48 +436,47 @@ public class NotificationManager
}
/**
- * Returns the AutomaticZenRule with the given name, if it exists and the caller has access.
+ * Returns the AutomaticZenRule with the given id, if it exists and the caller has access.
*
* <p>
* Only available if policy access is granted to this package.
* See {@link #isNotificationPolicyAccessGranted}.
*
* <p>
- * Returns null if there are no zen rules that match the given name, or if the calling package
+ * Returns null if there are no zen rules that match the given id, or if the calling package
* doesn't own the matching rule. See {@link AutomaticZenRule#getOwner}.
*/
- public AutomaticZenRule getAutomaticZenRule(String name) {
+ public AutomaticZenRule getAutomaticZenRule(String id) {
INotificationManager service = getService();
try {
- return service.getAutomaticZenRule(name);
+ return service.getAutomaticZenRule(id);
} catch (RemoteException e) {
}
return null;
}
/**
- * Creates or updates the given zen rule.
+ * Creates the given zen rule.
*
* <p>
* Only available if policy access is granted to this package.
* See {@link #isNotificationPolicyAccessGranted}.
*
- * <p>
- * Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
- * @param automaticZenRule the rule to create or update.
- * @return Whether the rule was successfully created or updated.
+ * @param automaticZenRule the rule to create.
+ * @return A fully populated {@link AutomaticZenRule} if the rule was persisted successfully,
+ * null otherwise.
*/
- public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule) {
+ public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule) {
INotificationManager service = getService();
try {
- return service.addOrUpdateAutomaticZenRule(automaticZenRule);
+ return service.addAutomaticZenRule(automaticZenRule);
} catch (RemoteException e) {
}
- return false;
+ return null;
}
/**
- * Renames a zen rule.
+ * Updates the given zen rule.
*
* <p>
* Only available if policy access is granted to this package.
@@ -485,21 +484,20 @@ public class NotificationManager
*
* <p>
* Callers can only update rules that they own. See {@link AutomaticZenRule#getOwner}.
- * @param oldName The name of the rule to update.
- * @param newName The new name for the rule.
+ * @param automaticZenRule the rule to update.
* @return Whether the rule was successfully updated.
*/
- public boolean renameAutomaticZenRule(String oldName, String newName) {
+ public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule) {
INotificationManager service = getService();
try {
- return service.renameAutomaticZenRule(oldName, newName);
+ return service.updateAutomaticZenRule(automaticZenRule);
} catch (RemoteException e) {
}
return false;
}
/**
- * Deletes the automatic zen rule with the given name.
+ * Deletes the automatic zen rule with the given id.
*
* <p>
* Only available if policy access is granted to this package.
@@ -507,13 +505,13 @@ public class NotificationManager
*
* <p>
* Callers can only delete rules that they own. See {@link AutomaticZenRule#getOwner}.
- * @param name the name of the rule to delete.
+ * @param id the id of the rule to delete.
* @return Whether the rule was successfully deleted.
*/
- public boolean removeAutomaticZenRule(String name) {
+ public boolean removeAutomaticZenRule(String id) {
INotificationManager service = getService();
try {
- return service.removeAutomaticZenRule(name);
+ return service.removeAutomaticZenRule(id);
} catch (RemoteException e) {
}
return false;
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index a1bb40c9298d..84b6d392b83e 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -227,24 +227,6 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
public static final String ACTION_PROFILE_PROVISIONING_COMPLETE =
"android.app.action.PROFILE_PROVISIONING_COMPLETE";
- /**
- * @hide
- * Broadcast Action: This broadcast is sent to indicate that the system is ready for the device
- * initializer to perform user setup tasks. This is only applicable to devices managed by a
- * device owner app.
- *
- * <p>The broadcast will be limited to the {@link DeviceAdminReceiver} component specified in
- * the device initializer field of the original intent or NFC bump that started the provisioning
- * process. You will generally handle this in
- * {@link DeviceAdminReceiver#onReadyForUserInitialization}.
- *
- * <p>Input: Nothing.</p>
- * <p>Output: Nothing</p>
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_READY_FOR_USER_INITIALIZATION =
- "android.app.action.READY_FOR_USER_INITIALIZATION";
-
/** @hide */
public static final String ACTION_CHOOSE_PRIVATE_KEY_ALIAS = "android.app.action.CHOOSE_PRIVATE_KEY_ALIAS";
@@ -435,23 +417,13 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
/**
* Called during provisioning of a managed device to allow the device initializer to perform
- * user setup steps. Only device initializers should override this method.
- *
- * <p> Called when the DeviceAdminReceiver receives an
- * android.app.action.ACTION_READY_FOR_USER_INITIALIZATION broadcast. As a prerequisite for the
- * execution of this callback the {@link DeviceAdminReceiver} has
- * to declare an intent filter for android.app.action.ACTION_READY_FOR_USER_INITIALIZATION. Only
- * the component specified in the device initializer component name field of the
- * original intent or NFC bump that started the provisioning process will receive this callback.
- *
- * <p>It is not assumed that the device initializer is finished when it returns from
- * this call, as it may do additional setup asynchronously. The device initializer must enable
- * the current user when it has finished any additional setup (such as adding an account by
- * using the {@link AccountManager}) in order for the user to be functional.
+ * user setup steps.
*
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
+ * @deprecated Do not use
*/
+ @Deprecated
@SystemApi
public void onReadyForUserInitialization(Context context, Intent intent) {
}
@@ -549,8 +521,6 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
onLockTaskModeEntering(context, intent, pkg);
} else if (ACTION_LOCK_TASK_EXITING.equals(action)) {
onLockTaskModeExiting(context, intent);
- } else if (ACTION_READY_FOR_USER_INITIALIZATION.equals(action)) {
- onReadyForUserInitialization(context, intent);
} else if (ACTION_NOTIFY_PENDING_SYSTEM_UPDATE.equals(action)) {
long receivedTime = intent.getLongExtra(EXTRA_SYSTEM_UPDATE_RECEIVED_TIME, -1);
onSystemUpdatePending(context, intent, receivedTime);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a118f167ae5c..b45f3b41929a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -497,96 +497,6 @@ public class DevicePolicyManager {
"android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
/**
- * @hide
- * On devices managed by a device owner app, a {@link ComponentName} extra indicating the
- * component of the application that is temporarily granted device owner privileges during
- * device initialization and profile owner privileges during secondary user initialization.
- *
- * <p>
- * It can also be used in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts
- * device owner provisioning via an NFC bump. For the NFC record, it should be flattened to a
- * string first.
- *
- * @see ComponentName#flattenToShortString()
- */
- public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME
- = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME";
-
- /**
- * @hide
- * A String extra holding an http url that specifies the download location of the device
- * initializer package. When not provided it is assumed that the device initializer package is
- * already installed.
- *
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
- * provisioning via an NFC bump.
- */
- public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION
- = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION";
-
- /**
- * @hide
- * An int extra holding a minimum required version code for the device initializer package.
- * If the initializer is already installed on the device, it will only be re-downloaded from
- * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION} if the version of
- * the installed package is less than this version code.
- *
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
- * provisioning via an NFC bump.
- */
- public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE
- = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE";
-
- /**
- * @hide
- * A String extra holding a http cookie header which should be used in the http request to the
- * url specified in {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}.
- *
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
- * provisioning via an NFC bump.
- */
- public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER
- = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
-
- /**
- * @hide
- * A String extra holding the URL-safe base64 encoded SHA-256 checksum of the file at download
- * location specified in
- * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}.
- *
- * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM}
- * should be present. The provided checksum should match the checksum of the file at the
- * download location. If the checksum doesn't match an error will be shown to the user and the
- * user will be asked to factory reset the device.
- *
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
- * provisioning via an NFC bump.
- */
- public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM
- = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM";
-
- /**
- * @hide
- * A String extra holding the URL-safe base64 encoded SHA-256 checksum of any signature of the
- * android package archive at the download location specified in {@link
- * #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}.
- *
- * <p>The signatures of an android package archive can be obtained using
- * {@link android.content.pm.PackageManager#getPackageArchiveInfo} with flag
- * {@link android.content.pm.PackageManager#GET_SIGNATURES}.
- *
- * <p>Either this extra or {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM}
- * should be present. The provided checksum should match the checksum of any signature of the
- * file at the download location. If the checksum doesn't match an error will be shown to the
- * user and the user will be asked to factory reset the device.
- *
- * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
- * provisioning via an NFC bump.
- */
- public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM
- = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_SIGNATURE_CHECKSUM";
-
- /**
* This MIME type is used for starting the Device Owner provisioning.
*
* <p>During device owner provisioning a device admin app is set as the owner of the device.
@@ -629,44 +539,6 @@ public class DevicePolicyManager {
public static final String MIME_TYPE_PROVISIONING_NFC
= "application/com.android.managedprovisioning";
-
- /**
- * @hide
- * This MIME type is used for starting the Device Owner provisioning that requires
- * new provisioning features introduced in API version
- * {@link android.os.Build.VERSION_CODES#M} in addition to those supported in earlier
- * versions.
- *
- * <p>During device owner provisioning a device admin app is set as the owner of the device.
- * A device owner has full control over the device. The device owner can not be modified by the
- * user.
- *
- * <p> A typical use case would be a device that is owned by a company, but used by either an
- * employee or client.
- *
- * <p> The NFC message should be sent to an unprovisioned device.
- *
- * <p>The NFC record must contain a serialized {@link java.util.Properties} object which
- * contains the following properties in addition to properties listed at
- * {@link #MIME_TYPE_PROVISIONING_NFC}:
- * <ul>
- * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
- * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}.
- * Replaces {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}. The value of the property
- * should be converted to a String via
- * {@link android.content.ComponentName#flattenToString()}</li>
- * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE}, optional</li></ul>
- *
- * <p> When device owner provisioning has completed, an intent of the type
- * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcasted to the
- * device owner.
- *
- * <p>
- * If provisioning fails, the device is factory reset.
- */
- public static final String MIME_TYPE_PROVISIONING_NFC_V2
- = "application/com.android.managedprovisioning.v2";
-
/**
* Activity action: ask the user to add a new device administrator to the system.
* The desired policy is the ComponentName of the policy in the
@@ -2701,28 +2573,28 @@ public class DevicePolicyManager {
/**
* @hide
* Sets the given package as the device owner.
- * Same as {@link #setDeviceOwner(String, String)} but without setting a device owner name.
- * @param packageName the package name of the application to be registered as the device owner.
+ * Same as {@link #setDeviceOwner(ComponentName, String)} but without setting a device owner name.
+ * @param who the component name to be registered as device owner.
* @return whether the package was successfully registered as the device owner.
* @throws IllegalArgumentException if the package name is null or invalid
* @throws IllegalStateException If the preconditions mentioned are not met.
*/
- public boolean setDeviceOwner(String packageName) {
- return setDeviceOwner(packageName, null);
+ public boolean setDeviceOwner(ComponentName who) {
+ return setDeviceOwner(who, null);
}
/**
* @hide
*/
- public boolean setDeviceOwner(String packageName, int userId) {
- return setDeviceOwner(packageName, null, userId);
+ public boolean setDeviceOwner(ComponentName who, int userId) {
+ return setDeviceOwner(who, null, userId);
}
/**
* @hide
*/
- public boolean setDeviceOwner(String packageName, String ownerName) {
- return setDeviceOwner(packageName, ownerName, UserHandle.USER_SYSTEM);
+ public boolean setDeviceOwner(ComponentName who, String ownerName) {
+ return setDeviceOwner(who, ownerName, UserHandle.USER_SYSTEM);
}
/**
@@ -2733,18 +2605,18 @@ public class DevicePolicyManager {
* this method.
* Calling this after the setup phase of the primary user has completed is allowed only if
* the caller is the shell uid, and there are no additional users and no accounts.
- * @param packageName the package name of the application to be registered as the device owner.
+ * @param who the component name to be registered as device owner.
* @param ownerName the human readable name of the institution that owns this device.
* @param userId ID of the user on which the device owner runs.
* @return whether the package was successfully registered as the device owner.
* @throws IllegalArgumentException if the package name is null or invalid
* @throws IllegalStateException If the preconditions mentioned are not met.
*/
- public boolean setDeviceOwner(String packageName, String ownerName, int userId)
+ public boolean setDeviceOwner(ComponentName who, String ownerName, int userId)
throws IllegalArgumentException, IllegalStateException {
if (mService != null) {
try {
- return mService.setDeviceOwner(packageName, ownerName, userId);
+ return mService.setDeviceOwner(who, ownerName, userId);
} catch (RemoteException re) {
Log.w(TAG, "Failed to set device owner");
}
@@ -2763,14 +2635,15 @@ public class DevicePolicyManager {
* the setup process.
* @param packageName the package name of the app, to compare with the registered device owner
* app, if any.
- * @return whether or not the package is registered as the device owner app.
+ * @return whether or not the package is registered as the device owner app. Note this method
+ * does *not* check weather the device owner is actually running on the current user.
*/
public boolean isDeviceOwnerApp(String packageName) {
if (mService != null) {
try {
- return mService.isDeviceOwner(packageName);
- } catch (RemoteException re) {
- Log.w(TAG, "Failed to check device owner");
+ return mService.isDeviceOwnerPackage(packageName);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
}
}
return false;
@@ -2785,6 +2658,17 @@ public class DevicePolicyManager {
}
/**
+ * Check whether a given component is registered as a device owner.
+ * Note this method does *not* check weather the device owner is actually running on the current
+ * user.
+ *
+ * @hide
+ */
+ public boolean isDeviceOwner(ComponentName who) {
+ return (who != null) && who.equals(getDeviceOwner());
+ }
+
+ /**
* Clears the current device owner. The caller must be the device owner.
*
* This function should be used cautiously as once it is called it cannot
@@ -2803,20 +2687,24 @@ public class DevicePolicyManager {
}
}
- /** @hide */
+ /**
+ * Returns the device owner package name. Note this method will still return the device owner
+ * package name even if it's running on a different user.
+ *
+ * @hide
+ */
@SystemApi
public String getDeviceOwner() {
- if (mService != null) {
- try {
- return mService.getDeviceOwner();
- } catch (RemoteException re) {
- Log.w(TAG, "Failed to get device owner");
- }
- }
- return null;
+ final ComponentName componentName = getDeviceOwnerComponent();
+ return componentName == null ? null : componentName.getPackageName();
}
- /** @hide */
+ /**
+ * Returns the device owner name. Note this method will still return the device owner
+ * name even if it's running on a different user.
+ *
+ * @hide
+ */
public String getDeviceOwnerName() {
if (mService != null) {
try {
@@ -2829,135 +2717,44 @@ public class DevicePolicyManager {
}
/**
- * @hide
- * Sets the given component as the device initializer. The package must already be installed and
- * set as an active device administrator, and there must not be an existing device initializer,
- * for this call to succeed. This method can only be called by an app holding the
- * MANAGE_DEVICE_ADMINS permission before the device is provisioned or by a device owner app. A
- * device initializer app is granted device owner privileges during device initialization and
- * profile owner privileges during secondary user initialization.
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
- * {@code null} if not called by the device owner.
- * @param initializer Which {@link DeviceAdminReceiver} to make device initializer.
- * @return whether the component was successfully registered as the device initializer.
- * @throws IllegalArgumentException if the componentname is null or invalid
- * @throws IllegalStateException if the caller is not device owner or the device has
- * already been provisioned or a device initializer already exists.
- */
- public boolean setDeviceInitializer(@Nullable ComponentName admin,
- @NonNull ComponentName initializer)
- throws IllegalArgumentException, IllegalStateException {
- if (mService != null) {
- try {
- return mService.setDeviceInitializer(admin, initializer);
- } catch (RemoteException re) {
- Log.w(TAG, "Failed to set device initializer");
- }
- }
- return false;
- }
-
- /**
- * @hide
- * Used to determine if a particular package has been registered as the device initializer.
+ * Returns the device owner component name. Note this method will still return the device owner
+ * component name even if it's running on a different user.
*
- * @param packageName the package name of the app, to compare with the registered device
- * initializer app, if any.
- * @return whether or not the caller is registered as the device initializer app.
- */
- public boolean isDeviceInitializerApp(String packageName) {
- if (mService != null) {
- try {
- return mService.isDeviceInitializer(packageName);
- } catch (RemoteException re) {
- Log.w(TAG, "Failed to check device initializer");
- }
- }
- return false;
- }
-
- /**
* @hide
- * Removes the device initializer, so that it will not be invoked on user initialization for any
- * subsequently created users. This method can be called by either the device owner or device
- * initializer itself. The caller must be an active administrator.
- *
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
*/
- public void clearDeviceInitializerApp(@NonNull ComponentName admin) {
+ public ComponentName getDeviceOwnerComponent() {
if (mService != null) {
try {
- mService.clearDeviceInitializer(admin);
+ return mService.getDeviceOwner();
} catch (RemoteException re) {
- Log.w(TAG, "Failed to clear device initializer");
+ Log.w(TAG, "Failed to get device owner");
}
}
+ return null;
}
/**
* @hide
- * Gets the device initializer of the system.
- *
- * @return the package name of the device initializer.
+ * @deprecated Do not use
+ * @removed
*/
+ @Deprecated
@SystemApi
public String getDeviceInitializerApp() {
- if (mService != null) {
- try {
- return mService.getDeviceInitializer();
- } catch (RemoteException re) {
- Log.w(TAG, "Failed to get device initializer");
- }
- }
return null;
}
/**
* @hide
- * Gets the device initializer component of the system.
- *
- * @return the component name of the device initializer.
+ * @deprecated Do not use
+ * @removed
*/
+ @Deprecated
@SystemApi
public ComponentName getDeviceInitializerComponent() {
- if (mService != null) {
- try {
- return mService.getDeviceInitializerComponent();
- } catch (RemoteException re) {
- Log.w(TAG, "Failed to get device initializer");
- }
- }
return null;
}
-
- /**
- * @hide
- * Sets the enabled state of the user. A user should be enabled only once it is ready to
- * be used.
- *
- * <p>Device initializer must call this method to mark the user as functional.
- * Only the device initializer agent can call this.
- *
- * <p>When the user is enabled, if the device initializer is not also the device owner, the
- * device initializer will no longer have elevated permissions to call methods in this class.
- * Additionally, it will be removed as an active administrator and its
- * {@link DeviceAdminReceiver} will be disabled.
- *
- * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @return whether the user is now enabled.
- */
- public boolean setUserEnabled(@NonNull ComponentName admin) {
- if (mService != null) {
- try {
- return mService.setUserEnabled(admin);
- } catch (RemoteException e) {
- Log.w(TAG, "Failed talking with device policy service", e);
- }
- }
- return false;
- }
-
/**
* @hide
* @deprecated Use #ACTION_SET_PROFILE_OWNER
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 55a21c61f53a..ccaa8cb82170 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -113,9 +113,9 @@ interface IDevicePolicyManager {
void reportFailedPasswordAttempt(int userHandle);
void reportSuccessfulPasswordAttempt(int userHandle);
- boolean setDeviceOwner(String packageName, String ownerName, int userId);
- boolean isDeviceOwner(String packageName);
- String getDeviceOwner();
+ boolean setDeviceOwner(in ComponentName who, String ownerName, int userId);
+ boolean isDeviceOwnerPackage(String packageName);
+ ComponentName getDeviceOwner();
String getDeviceOwnerName();
void clearDeviceOwner(String packageName);
@@ -211,13 +211,6 @@ interface IDevicePolicyManager {
boolean isRemovingAdmin(in ComponentName adminReceiver, int userHandle);
- boolean setUserEnabled(in ComponentName who);
- boolean isDeviceInitializer(String packageName);
- void clearDeviceInitializer(in ComponentName who);
- boolean setDeviceInitializer(in ComponentName who, in ComponentName initializer);
- String getDeviceInitializer();
- ComponentName getDeviceInitializerComponent();
-
void setUserIcon(in ComponentName admin, in Bitmap icon);
void setSystemUpdatePolicy(in ComponentName who, in SystemUpdatePolicy policy);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 9341be16c28f..32830053fae2 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -868,6 +868,9 @@ public class PackageInstaller {
public static final int MODE_INHERIT_EXISTING = 2;
/** {@hide} */
+ public static final int UID_UNKNOWN = -1;
+
+ /** {@hide} */
public int mode = MODE_INVALID;
/** {@hide} */
public int installFlags;
@@ -886,6 +889,8 @@ public class PackageInstaller {
/** {@hide} */
public Uri originatingUri;
/** {@hide} */
+ public int originatingUid = UID_UNKNOWN;
+ /** {@hide} */
public Uri referrerUri;
/** {@hide} */
public String abiOverride;
@@ -915,6 +920,7 @@ public class PackageInstaller {
appIcon = source.readParcelable(null);
appLabel = source.readString();
originatingUri = source.readParcelable(null);
+ originatingUid = source.readInt();
referrerUri = source.readParcelable(null);
abiOverride = source.readString();
volumeUuid = source.readString();
@@ -983,6 +989,15 @@ public class PackageInstaller {
}
/**
+ * Sets the UID that initiated package installation. Used for verification purposes.
+ *
+ * @see PackageManager#EXTRA_VERIFICATION_INSTALLER_UID
+ */
+ public void setOriginatingUid(int originatingUid) {
+ this.originatingUid = originatingUid;
+ }
+
+ /**
* Optionally set the URI that referred you to install this package. Used
* for verification purposes.
*
@@ -1022,6 +1037,11 @@ public class PackageInstaller {
}
/** {@hide} */
+ public void setInstallFlagsForcePermissionPrompt() {
+ installFlags |= PackageManager.INSTALL_FORCE_PERMISSION_PROMPT;
+ }
+
+ /** {@hide} */
public void dump(IndentingPrintWriter pw) {
pw.printPair("mode", mode);
pw.printHexPair("installFlags", installFlags);
@@ -1031,6 +1051,7 @@ public class PackageInstaller {
pw.printPair("appIcon", (appIcon != null));
pw.printPair("appLabel", appLabel);
pw.printPair("originatingUri", originatingUri);
+ pw.printPair("originatingUid", originatingUid);
pw.printPair("referrerUri", referrerUri);
pw.printPair("abiOverride", abiOverride);
pw.printPair("volumeUuid", volumeUuid);
@@ -1053,6 +1074,7 @@ public class PackageInstaller {
dest.writeParcelable(appIcon, flags);
dest.writeString(appLabel);
dest.writeParcelable(originatingUri, flags);
+ dest.writeInt(originatingUid);
dest.writeParcelable(referrerUri, flags);
dest.writeString(abiOverride);
dest.writeString(volumeUuid);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 697b946eec73..82cbbbeb39fc 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -422,6 +422,15 @@ public abstract class PackageManager {
public static final int INSTALL_FORCE_VOLUME_UUID = 0x00000200;
/**
+ * Flag parameter for {@link #installPackage} to indicate that we always want to force
+ * the prompt for permission approval. This overrides any special behaviour for internal
+ * components.
+ *
+ * @hide
+ */
+ public static final int INSTALL_FORCE_PERMISSION_PROMPT = 0x00000400;
+
+ /**
* Flag parameter for
* {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
* that you don't want to kill the app containing the component. Be careful when you set this
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 04b1a3bd2369..1a8602b19c06 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3201,15 +3201,12 @@ public class PackageParser {
}
a.info.resizeable = sa.getBoolean(
- R.styleable.AndroidManifestActivity_resizeableActivity, false);
- if (a.info.resizeable) {
- // Fixed screen orientation isn't supported with resizeable activities.
- a.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
- } else {
- a.info.screenOrientation = sa.getInt(
- R.styleable.AndroidManifestActivity_screenOrientation,
- ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- }
+ R.styleable.AndroidManifestActivity_resizeableActivity,
+ owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.N);
+
+ a.info.screenOrientation = sa.getInt(
+ R.styleable.AndroidManifestActivity_screenOrientation,
+ ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
a.info.lockTaskLaunchMode =
sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 20b0be11e23c..8f45f72de6e2 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -46,7 +46,7 @@ import java.lang.annotation.RetentionPolicy;
* provide substantially improved capabilities over the older camera
* API. Applications that target the limited level devices will run unchanged on
* the full-level devices; if your application requires a full-level device for
- * proper operation, declare the "android.hardware.camera2.full" feature in your
+ * proper operation, declare the "android.hardware.camera.level.full" feature in your
* manifest.</p>
*
* @see CameraManager#openCamera
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index e965d654fcc7..3f566eb92756 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1545,7 +1545,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
/**
* <p>Whether video stabilization is
* active.</p>
- * <p>Video stabilization automatically translates and scales images from
+ * <p>Video stabilization automatically warps images from
* the camera in order to stabilize motion between consecutive frames.</p>
* <p>If enabled, video stabilization can modify the
* {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream stabilized.</p>
@@ -1555,6 +1555,15 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* the video stabilization modes in the first several capture results may
* still be "OFF", and it will become "ON" when the initialization is
* done.</p>
+ * <p>In addition, not all recording sizes or frame rates may be supported for
+ * stabilization by a device that reports stabilization support. It is guaranteed
+ * that an output targeting a MediaRecorder or MediaCodec will be stabilized if
+ * the recording resolution is less than or equal to 1920 x 1080 (width less than
+ * or equal to 1920, height less than or equal to 1080), and the recording
+ * frame rate is less than or equal to 30fps. At other sizes, the CaptureResult
+ * {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return
+ * OFF if the recording output is not stabilized, or if there are no output
+ * Surface types that can be stabilized.</p>
* <p>If a camera device supports both this mode and OIS
* ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
* produce undesirable interaction, so it is recommended not to enable
@@ -1566,6 +1575,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* </ul></p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
* @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 46eddb3f6651..b3acf2b09784 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2056,7 +2056,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
/**
* <p>Whether video stabilization is
* active.</p>
- * <p>Video stabilization automatically translates and scales images from
+ * <p>Video stabilization automatically warps images from
* the camera in order to stabilize motion between consecutive frames.</p>
* <p>If enabled, video stabilization can modify the
* {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream stabilized.</p>
@@ -2066,6 +2066,15 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* the video stabilization modes in the first several capture results may
* still be "OFF", and it will become "ON" when the initialization is
* done.</p>
+ * <p>In addition, not all recording sizes or frame rates may be supported for
+ * stabilization by a device that reports stabilization support. It is guaranteed
+ * that an output targeting a MediaRecorder or MediaCodec will be stabilized if
+ * the recording resolution is less than or equal to 1920 x 1080 (width less than
+ * or equal to 1920, height less than or equal to 1080), and the recording
+ * frame rate is less than or equal to 30fps. At other sizes, the CaptureResult
+ * {@link CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE android.control.videoStabilizationMode} field will return
+ * OFF if the recording output is not stabilized, or if there are no output
+ * Surface types that can be stabilized.</p>
* <p>If a camera device supports both this mode and OIS
* ({@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode}), turning both modes on may
* produce undesirable interaction, so it is recommended not to enable
@@ -2077,6 +2086,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* </ul></p>
* <p>This key is available on all devices.</p>
*
+ * @see CaptureRequest#CONTROL_VIDEO_STABILIZATION_MODE
* @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
* @see CaptureRequest#SCALER_CROP_REGION
* @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 87c063ff3eb0..97bd5d284c71 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -21,7 +21,6 @@ import android.os.Parcel;
import android.text.TextUtils;
import android.util.Log;
-import java.net.InetAddress;
import java.net.Inet4Address;
import java.util.Objects;
@@ -34,7 +33,7 @@ import java.util.Objects;
public class DhcpResults extends StaticIpConfiguration {
private static final String TAG = "DhcpResults";
- public InetAddress serverAddress;
+ public Inet4Address serverAddress;
/** Vendor specific information (from RFC 2132). */
public String vendorInfo;
@@ -142,7 +141,7 @@ public class DhcpResults extends StaticIpConfiguration {
private static void readFromParcel(DhcpResults dhcpResults, Parcel in) {
StaticIpConfiguration.readFromParcel(dhcpResults, in);
dhcpResults.leaseDuration = in.readInt();
- dhcpResults.serverAddress = NetworkUtils.unparcelInetAddress(in);
+ dhcpResults.serverAddress = (Inet4Address) NetworkUtils.unparcelInetAddress(in);
dhcpResults.vendorInfo = in.readString();
}
@@ -183,8 +182,8 @@ public class DhcpResults extends StaticIpConfiguration {
public boolean setServerAddress(String addrString) {
try {
- serverAddress = NetworkUtils.numericToInetAddress(addrString);
- } catch (IllegalArgumentException e) {
+ serverAddress = (Inet4Address) NetworkUtils.numericToInetAddress(addrString);
+ } catch (IllegalArgumentException|ClassCastException e) {
Log.e(TAG, "setServerAddress failed with addrString " + addrString);
return true;
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index c4de4a21e093..6bfa2a495b82 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -667,7 +667,8 @@ public final class LinkProperties implements Parcelable {
* @return {@code true} if there is an IPv4 address, {@code false} otherwise.
*/
private boolean hasIPv4AddressOnInterface(String iface) {
- return (mIfaceName.equals(iface) && hasIPv4Address()) ||
+ // mIfaceName can be null.
+ return (Objects.equals(iface, mIfaceName) && hasIPv4Address()) ||
(iface != null && mStackedLinks.containsKey(iface) &&
mStackedLinks.get(iface).hasIPv4Address());
}
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index c3733085caf6..cd483b104489 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -19,6 +19,7 @@ package android.os;
import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.MathUtils;
import java.io.Serializable;
import java.util.ArrayList;
@@ -1345,18 +1346,19 @@ public class BaseBundle {
*/
void readFromParcelInner(Parcel parcel) {
int length = parcel.readInt();
- if (length < 0) {
- throw new RuntimeException("Bad length in parcel: " + length);
- }
readFromParcelInner(parcel, length);
}
private void readFromParcelInner(Parcel parcel, int length) {
- if (length == 0) {
+ if (length < 0) {
+ throw new RuntimeException("Bad length in parcel: " + length);
+
+ } else if (length == 0) {
// Empty Bundle or end of data.
mParcelledData = EMPTY_PARCEL;
return;
}
+
int magic = parcel.readInt();
if (magic != BUNDLE_MAGIC) {
//noinspection ThrowableInstanceNeverThrown
@@ -1366,7 +1368,7 @@ public class BaseBundle {
// Advance within this Parcel
int offset = parcel.dataPosition();
- parcel.setDataPosition(offset + length);
+ parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
Parcel p = Parcel.obtain();
p.setDataPosition(0);
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 23748993f217..85041adbe975 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -634,6 +634,11 @@ public class Build {
* M comes after L.
*/
public static final int M = 23;
+
+ /**
+ * N is for ¯\_(ツ)_/¯.
+ */
+ public static final int N = CUR_DEVELOPMENT;
}
/** The type of build, like "user" or "eng". */
@@ -691,6 +696,9 @@ public class Build {
* @hide
*/
public static boolean isBuildConsistent() {
+ // Don't care on eng builds. Incremental build may trigger false negative.
+ if ("eng".equals(TYPE)) return true;
+
final String system = SystemProperties.get("ro.build.fingerprint");
final String vendor = SystemProperties.get("ro.vendor.build.fingerprint");
final String bootimage = SystemProperties.get("ro.bootimage.build.fingerprint");
diff --git a/core/java/android/os/ParcelableParcel.java b/core/java/android/os/ParcelableParcel.java
index 11785f1072fd..5bbe6488d514 100644
--- a/core/java/android/os/ParcelableParcel.java
+++ b/core/java/android/os/ParcelableParcel.java
@@ -16,6 +16,8 @@
package android.os;
+import android.util.MathUtils;
+
/**
* Parcelable containing a raw Parcel of data.
* @hide
@@ -33,9 +35,13 @@ public class ParcelableParcel implements Parcelable {
mParcel = Parcel.obtain();
mClassLoader = loader;
int size = src.readInt();
+ if (size < 0) {
+ throw new IllegalArgumentException("Negative size read from parcel");
+ }
+
int pos = src.dataPosition();
- mParcel.appendFrom(src, src.dataPosition(), size);
- src.setDataPosition(pos + size);
+ src.setDataPosition(MathUtils.addOrThrow(pos, size));
+ mParcel.appendFrom(src, pos, size);
}
public Parcel getParcel() {
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index 66642debc032..db04c7125adc 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -214,6 +214,9 @@ public abstract class PreferenceFragment extends Fragment implements
@Override
public void onDestroyView() {
+ if (mList != null) {
+ mList.setOnKeyListener(null);
+ }
mList = null;
mHandler.removeCallbacks(mRequestFocus);
mHandler.removeMessages(MSG_BIND_PREFERENCES);
diff --git a/core/java/android/preference/PreferenceGroup.java b/core/java/android/preference/PreferenceGroup.java
index d6e9e61f358b..13c36614729e 100644
--- a/core/java/android/preference/PreferenceGroup.java
+++ b/core/java/android/preference/PreferenceGroup.java
@@ -148,16 +148,15 @@ public abstract class PreferenceGroup extends Preference implements GenericInfla
}
}
- int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
- if (insertionIndex < 0) {
- insertionIndex = insertionIndex * -1 - 1;
- }
-
if (!onPrepareAddPreference(preference)) {
return false;
}
synchronized(this) {
+ int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
+ if (insertionIndex < 0) {
+ insertionIndex = insertionIndex * -1 - 1;
+ }
mPreferenceList.add(insertionIndex, preference);
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 4f880b1275e4..342f8c77d6ee 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -153,6 +153,18 @@ public class CallLog {
/**
* The type of the call (incoming, outgoing or missed).
* <P>Type: INTEGER (int)</P>
+ *
+ * <p>
+ * Allowed values:
+ * <ul>
+ * <li>{@link #INCOMING_TYPE}</li>
+ * <li>{@link #OUTGOING_TYPE}</li>
+ * <li>{@link #MISSED_TYPE}</li>
+ * <li>{@link #VOICEMAIL_TYPE}</li>
+ * <li>{@link #REJECTED_TYPE}</li>
+ * <li>{@link #BLOCKED_TYPE}</li>
+ * </ul>
+ * </p>
*/
public static final String TYPE = "type";
@@ -164,6 +176,10 @@ public class CallLog {
public static final int MISSED_TYPE = 3;
/** Call log type for voicemails. */
public static final int VOICEMAIL_TYPE = 4;
+ /** Call log type for calls rejected by direct user action. */
+ public static final int REJECTED_TYPE = 5;
+ /** Call log type for calls blocked automatically. */
+ public static final int BLOCKED_TYPE = 6;
/**
* Bit-mask describing features of the call (e.g. video).
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 1b104e3500ff..241e6db91756 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -19,6 +19,7 @@ package android.provider;
import static android.net.TrafficStats.KB_IN_BYTES;
import static android.system.OsConstants.SEEK_SET;
+import android.annotation.Nullable;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
@@ -764,19 +765,33 @@ public final class DocumentsContract {
* @see #buildDocumentUri(String, String)
* @see #buildDocumentUriUsingTree(Uri, String)
*/
- public static boolean isDocumentUri(Context context, Uri uri) {
- final List<String> paths = uri.getPathSegments();
- if (paths.size() == 2 && PATH_DOCUMENT.equals(paths.get(0))) {
- return isDocumentsProvider(context, uri.getAuthority());
+ public static boolean isDocumentUri(Context context, @Nullable Uri uri) {
+ if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) {
+ final List<String> paths = uri.getPathSegments();
+ if (paths.size() == 2) {
+ return PATH_DOCUMENT.equals(paths.get(0));
+ } else if (paths.size() == 4) {
+ return PATH_TREE.equals(paths.get(0)) && PATH_DOCUMENT.equals(paths.get(2));
+ }
}
- if (paths.size() == 4 && PATH_TREE.equals(paths.get(0))
- && PATH_DOCUMENT.equals(paths.get(2))) {
- return isDocumentsProvider(context, uri.getAuthority());
+ return false;
+ }
+
+ /** {@hide} */
+ public static boolean isRootUri(Context context, @Nullable Uri uri) {
+ if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) {
+ final List<String> paths = uri.getPathSegments();
+ return (paths.size() == 2 && PATH_ROOT.equals(paths.get(0)));
}
return false;
}
/** {@hide} */
+ public static boolean isContentUri(@Nullable Uri uri) {
+ return uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme());
+ }
+
+ /** {@hide} */
public static boolean isTreeUri(Uri uri) {
final List<String> paths = uri.getPathSegments();
return (paths.size() >= 2 && PATH_TREE.equals(paths.get(0)));
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
index 2a8fb2c14695..c679eda2fff2 100644
--- a/core/java/android/service/notification/ConditionProviderService.java
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -35,8 +35,7 @@ import android.util.Log;
* the {@link android.Manifest.permission#BIND_CONDITION_PROVIDER_SERVICE} permission
* and include an intent filter with the {@link #SERVICE_INTERFACE} action. If you want users to be
* able to create and update conditions for this service to monitor, include the
- * {@link #META_DATA_RULE_TYPE}, {@link #META_DATA_DEFAULT_CONDITION_ID}, and
- * {@link #META_DATA_CONFIGURATION_ACTIVITY} tags. For example:</p>
+ * {@link #META_DATA_RULE_TYPE} and {@link #META_DATA_CONFIGURATION_ACTIVITY} tags. For example:</p>
* <pre>
* &lt;service android:name=".MyConditionProvider"
* android:label="&#64;string/service_name"
@@ -49,10 +48,6 @@ import android.util.Log;
* android:value="@string/my_condition_rule">
* &lt;/meta-data>
* &lt;meta-data
- * android:name="android.service.zen.automatic.defaultConditionId"
- * android:value="condition://com.my.package/mycondition">
- * &lt;/meta-data>
- * &lt;meta-data
* android:name="android.service.zen.automatic.configurationActivity"
* android:value="com.my.package/.MyConditionConfigurationActivity">
* &lt;/meta-data>
@@ -82,13 +77,6 @@ public abstract class ConditionProviderService extends Service {
public static final String META_DATA_RULE_TYPE = "android.service.zen.automatic.ruleType";
/**
- * The name of the {@code meta-data} tag containing a default Condition {@link Uri} that can
- * be parsed by this service.
- */
- public static final String META_DATA_DEFAULT_CONDITION_ID =
- "android.service.zen.automatic.defaultConditionId";
-
- /**
* The name of the {@code meta-data} tag containing the {@link ComponentName} of an activity
* that allows users to configure the conditions provided by this service.
*/
@@ -96,17 +84,9 @@ public abstract class ConditionProviderService extends Service {
"android.service.zen.automatic.configurationActivity";
/**
- * A condition {@link Uri} extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}. If the
- * condition Uri is modified by that activity, it must be included in the result Intent extras
- * in order to be persisted.
- */
- public static final String EXTRA_CONDITION_ID = "android.content.automatic.conditionId";
-
- /**
- * A String rule name extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}. This extra is
- * informative only, and will be ignored if included in the result Intent extras.
+ * A String rule id extra passed to {@link #META_DATA_CONFIGURATION_ACTIVITY}.
*/
- public static final String EXTRA_RULE_NAME = "android.content.automatic.ruleName";
+ public static final String EXTRA_RULE_ID = "android.content.automatic.ruleId";
/**
* Called when this service is connected.
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 4de903efff18..e054a61e7b62 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -110,6 +110,7 @@ public class ZenModeConfig implements Parcelable {
private static final String RULE_ATT_COMPONENT = "component";
private static final String RULE_ATT_ZEN = "zen";
private static final String RULE_ATT_CONDITION_ID = "conditionId";
+ private static final String RULE_ATT_CREATION_TIME = "creationTime";
public boolean allowCalls = DEFAULT_ALLOW_CALLS;
public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
@@ -415,6 +416,7 @@ public class ZenModeConfig implements Parcelable {
final String id = parser.getAttributeValue(null, RULE_ATT_ID);
final ZenRule automaticRule = readRuleXml(parser);
if (id != null && automaticRule != null) {
+ automaticRule.id = id;
rt.automaticRules.put(id, automaticRule);
}
}
@@ -468,6 +470,7 @@ public class ZenModeConfig implements Parcelable {
}
rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
+ rt.creationTime = safeLong(parser, RULE_ATT_CREATION_TIME, 0);
rt.condition = readConditionXml(parser);
return rt;
}
@@ -485,6 +488,7 @@ public class ZenModeConfig implements Parcelable {
if (rule.conditionId != null) {
out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString());
}
+ out.attribute(null, RULE_ATT_CREATION_TIME, Long.toString(rule.creationTime));
if (rule.condition != null) {
writeConditionXml(rule.condition, out);
}
@@ -552,6 +556,11 @@ public class ZenModeConfig implements Parcelable {
return Uri.parse(val);
}
+ private static long safeLong(XmlPullParser parser, String att, long defValue) {
+ final String val = parser.getAttributeValue(null, att);
+ return tryParseLong(val, defValue);
+ }
+
public ArraySet<String> getAutomaticRuleNames() {
final ArraySet<String> rt = new ArraySet<String>();
for (int i = 0; i < automaticRules.size(); i++) {
@@ -887,7 +896,7 @@ public class ZenModeConfig implements Parcelable {
return Global.isValidZenMode(rt) ? rt : defValue;
}
- public String newRuleId() {
+ public static String newRuleId() {
return UUID.randomUUID().toString().replace("-", "");
}
@@ -943,6 +952,8 @@ public class ZenModeConfig implements Parcelable {
public Uri conditionId; // required for automatic
public Condition condition; // optional
public ComponentName component; // optional
+ public String id; // required for automatic (unique)
+ public long creationTime; // required for automatic
public ZenRule() { }
@@ -956,6 +967,10 @@ public class ZenModeConfig implements Parcelable {
conditionId = source.readParcelable(null);
condition = source.readParcelable(null);
component = source.readParcelable(null);
+ if (source.readInt() == 1) {
+ id = source.readString();
+ }
+ creationTime = source.readLong();
}
@Override
@@ -977,6 +992,13 @@ public class ZenModeConfig implements Parcelable {
dest.writeParcelable(conditionId, 0);
dest.writeParcelable(condition, 0);
dest.writeParcelable(component, 0);
+ if (id != null) {
+ dest.writeInt(1);
+ dest.writeString(id);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeLong(creationTime);
}
@Override
@@ -989,6 +1011,8 @@ public class ZenModeConfig implements Parcelable {
.append(",conditionId=").append(conditionId)
.append(",condition=").append(condition)
.append(",component=").append(component)
+ .append(",id=").append(id)
+ .append(",creationTime=").append(creationTime)
.append(']').toString();
}
@@ -1029,6 +1053,12 @@ public class ZenModeConfig implements Parcelable {
if (!Objects.equals(component, to.component)) {
d.addLine(item, "component", component, to.component);
}
+ if (!Objects.equals(id, to.id)) {
+ d.addLine(item, "id", id, to.id);
+ }
+ if (creationTime == to.creationTime) {
+ d.addLine(item, "creationTime", creationTime, to.creationTime);
+ }
}
@Override
@@ -1042,13 +1072,15 @@ public class ZenModeConfig implements Parcelable {
&& other.zenMode == zenMode
&& Objects.equals(other.conditionId, conditionId)
&& Objects.equals(other.condition, condition)
- && Objects.equals(other.component, component);
+ && Objects.equals(other.component, component)
+ && Objects.equals(other.id, id)
+ && other.creationTime == creationTime;
}
@Override
public int hashCode() {
return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
- component);
+ component, id, creationTime);
}
public boolean isAutomaticActive() {
diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java
index 8b57d3d3d6c4..32aac13d39b4 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -156,6 +156,27 @@ public final class MathUtils {
return start + (stop - start) * amount;
}
+ /**
+ * Returns an interpolated angle in degrees between a set of start and end
+ * angles.
+ * <p>
+ * Unlike {@link #lerp(float, float, float)}, the direction and distance of
+ * travel is determined by the shortest angle between the start and end
+ * angles. For example, if the starting angle is 0 and the ending angle is
+ * 350, then the interpolated angle will be in the range [0,-10] rather
+ * than [0,350].
+ *
+ * @param start the starting angle in degrees
+ * @param end the ending angle in degrees
+ * @param amount the position between start and end in the range [0,1]
+ * where 0 is the starting angle and 1 is the ending angle
+ * @return the interpolated angle in degrees
+ */
+ public static float lerpDeg(float start, float end, float amount) {
+ final float minAngle = (((end - start) + 180) % 360) - 180;
+ return minAngle * amount + start;
+ }
+
public static float norm(float start, float stop, float value) {
return (value - start) / (stop - start);
}
@@ -185,4 +206,24 @@ public final class MathUtils {
public static void randomSeed(long seed) {
sRandom.setSeed(seed);
}
+
+ /**
+ * Returns the sum of the two parameters, or throws an exception if the resulting sum would
+ * cause an overflow or underflow.
+ * @throws IllegalArgumentException when overflow or underflow would occur.
+ */
+ public static int addOrThrow(int a, int b) throws IllegalArgumentException {
+ if (b == 0) {
+ return a;
+ }
+
+ if (b > 0 && a <= (Integer.MAX_VALUE - b)) {
+ return a + b;
+ }
+
+ if (b < 0 && a >= (Integer.MIN_VALUE - b)) {
+ return a + b;
+ }
+ throw new IllegalArgumentException("Addition overflow: " + a + " + " + b);
+ }
}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index acad49673694..cc4bcb6e2d77 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -80,20 +80,6 @@ oneway interface IWindow {
int localValue, int localChanges);
/**
- * The window is beginning to animate. The application should stop drawing frames until the
- * window is not animating anymore, indicated by being called {@link #windowEndAnimating}.
- *
- * @param remainingFrameCount how many frames the app might still draw before stopping drawing;
- * pass -1 to let it continue drawing
- */
- void onAnimationStarted(int remainingFrameCount);
-
- /**
- * The window has ended animating. See {@link #onAnimationStarted}.
- */
- void onAnimationStopped();
-
- /**
* Called for non-application windows when the enter animation has completed.
*/
void dispatchWindowShown();
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 7d48a9a11eda..db68c29d025f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -447,10 +447,11 @@ public class SurfaceView extends View {
final boolean formatChanged = mFormat != mRequestedFormat;
final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
final boolean visibleChanged = mVisible != mRequestedVisible;
+ final boolean layoutSizeChanged = getWidth() != mLayout.width || getHeight() != mLayout.height;
if (force || creating || formatChanged || sizeChanged || visibleChanged
|| mLeft != mLocation[0] || mTop != mLocation[1]
- || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
+ || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded || layoutSizeChanged) {
if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
+ " format=" + formatChanged + " size=" + sizeChanged
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index dcef14267b7f..304e9c048b60 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -349,7 +349,7 @@ public class ThreadedRenderer extends HardwareRenderer {
* @param right The right side of the protected bounds.
* @param bottom The bottom side of the protected bounds.
*/
- public void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
+ public void setContentDrawBounds(int left, int top, int right, int bottom) {
mStagedContentBounds.set(left, top, right, bottom);
}
@@ -370,9 +370,9 @@ public class ThreadedRenderer extends HardwareRenderer {
// renderer.
if (!mCurrentContentBounds.equals(mStagedContentBounds)) {
mCurrentContentBounds.set(mStagedContentBounds);
- nSetContentOverdrawProtectionBounds(mNativeProxy, mCurrentContentBounds.left,
- mCurrentContentBounds.top, mCurrentContentBounds.right,
- mCurrentContentBounds.bottom);
+ nSetContentDrawBounds(mNativeProxy, mCurrentContentBounds.left,
+ mCurrentContentBounds.top, mCurrentContentBounds.right,
+ mCurrentContentBounds.bottom);
}
attachInfo.mIgnoreDirtyState = false;
@@ -600,6 +600,6 @@ public class ThreadedRenderer extends HardwareRenderer {
boolean placeFront);
private static native void nRemoveRenderNode(long nativeProxy, long rootRenderNode);
private static native void nDrawRenderNode(long nativeProxy, long rootRenderNode);
- private static native void nSetContentOverdrawProtectionBounds(long nativeProxy, int left,
+ private static native void nSetContentDrawBounds(long nativeProxy, int left,
int top, int right, int bottom);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f6c60ed688a5..14e7d6c4e81e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -73,6 +73,7 @@ import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
+import android.view.WindowCallbacks;
import android.widget.Scroller;
import com.android.internal.R;
@@ -115,6 +116,12 @@ public final class ViewRootImpl implements ViewParent,
private static final boolean DEBUG_INPUT_STAGES = false || LOCAL_LOGV;
/**
+ * Set to false if we do not want to use the multi threaded renderer. Note that by disabling
+ * this, WindowCallbacks will not fire.
+ */
+ private static final boolean USE_MT_RENDERER = true;
+
+ /**
* Set this system property to true to force the view hierarchy to render
* at 60 Hz. This can be used to measure the potential framerate.
*/
@@ -132,12 +139,12 @@ public final class ViewRootImpl implements ViewParent,
static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
- static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();
+ static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
static boolean sFirstDrawComplete = false;
- static final ArrayList<ComponentCallbacks> sConfigCallbacks
- = new ArrayList<ComponentCallbacks>();
+ static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList();
+ final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList();
final Context mContext;
final IWindowSession mWindowSession;
final Display mDisplay;
@@ -231,11 +238,7 @@ public final class ViewRootImpl implements ViewParent,
boolean mNewSurfaceNeeded;
boolean mHasHadWindowFocus;
boolean mLastWasImTarget;
- boolean mWindowsAnimating;
- boolean mDrawDuringWindowsAnimating;
- /** How many frames the app is still allowed to draw when a window animation is happening. */
- private int mRemainingFrameCount;
boolean mIsDrawing;
int mLastSystemUiVisibility;
int mClientWindowLayoutFlags;
@@ -417,6 +420,22 @@ public final class ViewRootImpl implements ViewParent,
}
}
+ public void addWindowCallbacks(WindowCallbacks callback) {
+ if (USE_MT_RENDERER) {
+ synchronized (mWindowCallbacks) {
+ mWindowCallbacks.add(callback);
+ }
+ }
+ }
+
+ public void removeWindowCallbacks(WindowCallbacks callback) {
+ if (USE_MT_RENDERER) {
+ synchronized (mWindowCallbacks) {
+ mWindowCallbacks.remove(callback);
+ }
+ }
+ }
+
// FIXME for perf testing only
private boolean mProfile = false;
@@ -1378,6 +1397,7 @@ public final class ViewRootImpl implements ViewParent,
mAttachInfo.mWindowVisibility = viewVisibility;
host.dispatchWindowVisibilityChanged(viewVisibility);
if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
+ endDragResizing();
destroyHardwareResources();
}
if (viewVisibility == View.GONE) {
@@ -1701,14 +1721,20 @@ public final class ViewRootImpl implements ViewParent,
final boolean dragResizing = (relayoutResult
& WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING) != 0;
if (mDragResizing != dragResizing) {
- mDragResizing = dragResizing;
- mFullRedrawNeeded = true;
+ if (dragResizing) {
+ startDragResizing(frame);
+ } else {
+ // We shouldn't come here, but if we come we should end the resize.
+ endDragResizing();
+ }
}
- if (dragResizing) {
- mCanvasOffsetX = mWinFrame.left;
- mCanvasOffsetY = mWinFrame.top;
- } else {
- mCanvasOffsetX = mCanvasOffsetY = 0;
+ if (!USE_MT_RENDERER) {
+ if (dragResizing) {
+ mCanvasOffsetX = mWinFrame.left;
+ mCanvasOffsetY = mWinFrame.top;
+ } else {
+ mCanvasOffsetX = mCanvasOffsetY = 0;
+ }
}
} catch (RemoteException e) {
}
@@ -1948,8 +1974,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- boolean skipDraw = false;
-
if (mFirst) {
// handle first focus request
if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
@@ -1964,11 +1988,6 @@ public final class ViewRootImpl implements ViewParent,
+ mView.findFocus());
}
}
- } else if (mWindowsAnimating) {
- if (mRemainingFrameCount <= 0) {
- skipDraw = true;
- }
- mRemainingFrameCount--;
}
final boolean changedVisibility = (viewVisibilityChanged || mFirst) && isViewVisible;
@@ -2013,16 +2032,14 @@ public final class ViewRootImpl implements ViewParent,
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
if (!cancelDraw && !newSurface) {
- if (!skipDraw || mReportNextDraw) {
- if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
- for (int i = 0; i < mPendingTransitions.size(); ++i) {
- mPendingTransitions.get(i).startChangingAnimations();
- }
- mPendingTransitions.clear();
+ if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
+ for (int i = 0; i < mPendingTransitions.size(); ++i) {
+ mPendingTransitions.get(i).startChangingAnimations();
}
-
- performDraw();
+ mPendingTransitions.clear();
}
+
+ performDraw();
} else {
if (isViewVisible) {
// Try again
@@ -2549,6 +2566,14 @@ public final class ViewRootImpl implements ViewParent,
dirty.setEmpty();
+ // Stage the content drawn size now. It will be transferred to the renderer
+ // shortly before the draw commands get send to the renderer.
+ synchronized (mWindowCallbacks) {
+ for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+ mWindowCallbacks.get(i).onContentDraw(mWindowAttributes.surfaceInsets.left,
+ mWindowAttributes.surfaceInsets.top, mWidth, mHeight);
+ }
+ }
mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
} else {
// If we get here with a disabled & requested hardware renderer, something went
@@ -2757,16 +2782,6 @@ public final class ViewRootImpl implements ViewParent,
return mAttachInfo.mAccessibilityFocusDrawable;
}
- /**
- * @hide
- */
- public void setDrawDuringWindowsAnimating(boolean value) {
- mDrawDuringWindowsAnimating = value;
- if (value) {
- handleDispatchWindowAnimationStopped();
- }
- }
-
boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
final Rect ci = mAttachInfo.mContentInsets;
final Rect vi = mAttachInfo.mVisibleInsets;
@@ -3130,8 +3145,6 @@ public final class ViewRootImpl implements ViewParent,
private final static int MSG_WINDOW_MOVED = 23;
private final static int MSG_SYNTHESIZE_INPUT_EVENT = 24;
private final static int MSG_DISPATCH_WINDOW_SHOWN = 25;
- private final static int MSG_DISPATCH_WINDOW_ANIMATION_STOPPED = 26;
- private final static int MSG_DISPATCH_WINDOW_ANIMATION_STARTED = 27;
final class ViewRootHandler extends Handler {
@Override
@@ -3175,10 +3188,6 @@ public final class ViewRootImpl implements ViewParent,
return "MSG_PROCESS_INPUT_EVENTS";
case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST:
return "MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST";
- case MSG_DISPATCH_WINDOW_ANIMATION_STARTED:
- return "MSG_DISPATCH_WINDOW_ANIMATION_STARTED";
- case MSG_DISPATCH_WINDOW_ANIMATION_STOPPED:
- return "MSG_DISPATCH_WINDOW_ANIMATION_STOPPED";
case MSG_WINDOW_MOVED:
return "MSG_WINDOW_MOVED";
case MSG_SYNTHESIZE_INPUT_EVENT:
@@ -3399,13 +3408,6 @@ public final class ViewRootImpl implements ViewParent,
case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: {
setAccessibilityFocus(null, null);
} break;
- case MSG_DISPATCH_WINDOW_ANIMATION_STARTED: {
- int remainingFrameCount = msg.arg1;
- handleDispatchWindowAnimationStarted(remainingFrameCount);
- } break;
- case MSG_DISPATCH_WINDOW_ANIMATION_STOPPED: {
- handleDispatchWindowAnimationStopped();
- } break;
case MSG_INVALIDATE_WORLD: {
if (mView != null) {
invalidateWorld(mView);
@@ -4018,9 +4020,6 @@ public final class ViewRootImpl implements ViewParent,
if (q.mEvent instanceof KeyEvent) {
return processKeyEvent(q);
} else {
- // If delivering a new non-key event, make sure the window is
- // now allowed to start updating.
- handleDispatchWindowAnimationStopped();
final int source = q.mEvent.getSource();
if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
return processPointerEvent(q);
@@ -4047,12 +4046,6 @@ public final class ViewRootImpl implements ViewParent,
private int processKeyEvent(QueuedInputEvent q) {
final KeyEvent event = (KeyEvent)q.mEvent;
- if (event.getAction() != KeyEvent.ACTION_UP) {
- // If delivering a new key event, make sure the window is
- // now allowed to start updating.
- handleDispatchWindowAnimationStopped();
- }
-
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;
@@ -5267,22 +5260,6 @@ public final class ViewRootImpl implements ViewParent,
}
}
- public void handleDispatchWindowAnimationStarted(int remainingFrameCount) {
- if (!mDrawDuringWindowsAnimating && remainingFrameCount != -1) {
- mRemainingFrameCount = remainingFrameCount;
- mWindowsAnimating = true;
- }
- }
-
- public void handleDispatchWindowAnimationStopped() {
- if (mWindowsAnimating) {
- mWindowsAnimating = false;
- if (!mDirty.isEmpty() || mIsAnimating || mFullRedrawNeeded) {
- scheduleTraversals();
- }
- }
- }
-
public void handleDispatchWindowShown() {
mAttachInfo.mTreeObserver.dispatchOnWindowShown();
}
@@ -5623,6 +5600,17 @@ public final class ViewRootImpl implements ViewParent,
+ " contentInsets=" + contentInsets.toShortString()
+ " visibleInsets=" + visibleInsets.toShortString()
+ " reportDraw=" + reportDraw);
+
+ // Tell all listeners that we are resizing the window so that the chrome can get
+ // updated as fast as possible on a separate thread,
+ if (mDragResizing) {
+ synchronized (mWindowCallbacks) {
+ for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+ mWindowCallbacks.get(i).onWindowSizeIsChanging(frame);
+ }
+ }
+ }
+
Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(frame);
@@ -6194,15 +6182,6 @@ public final class ViewRootImpl implements ViewParent,
mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args));
}
- public void dispatchWindowAnimationStarted(int remainingFrameCount) {
- mHandler.obtainMessage(MSG_DISPATCH_WINDOW_ANIMATION_STARTED,
- remainingFrameCount, 0 /* unused */).sendToTarget();
- }
-
- public void dispatchWindowAnimationStopped() {
- mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_ANIMATION_STOPPED);
- }
-
public void dispatchCheckFocus() {
if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) {
// This will result in a call to checkFocus() below.
@@ -6763,22 +6742,6 @@ public final class ViewRootImpl implements ViewParent,
}
@Override
- public void onAnimationStarted(int remainingFrameCount) {
- final ViewRootImpl viewAncestor = mViewAncestor.get();
- if (viewAncestor != null) {
- viewAncestor.dispatchWindowAnimationStarted(remainingFrameCount);
- }
- }
-
- @Override
- public void onAnimationStopped() {
- final ViewRootImpl viewAncestor = mViewAncestor.get();
- if (viewAncestor != null) {
- viewAncestor.dispatchWindowAnimationStopped();
- }
- }
-
- @Override
public void dispatchWindowShown() {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
@@ -6804,6 +6767,36 @@ public final class ViewRootImpl implements ViewParent,
}
/**
+ * Start a drag resizing which will inform all listeners that a window resize is taking place.
+ */
+ private void startDragResizing(Rect initialBounds) {
+ if (!mDragResizing) {
+ mDragResizing = true;
+ synchronized (mWindowCallbacks) {
+ for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+ mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds);
+ }
+ }
+ mFullRedrawNeeded = true;
+ }
+ }
+
+ /**
+ * End a drag resize which will inform all listeners that a window resize has ended.
+ */
+ private void endDragResizing() {
+ if (mDragResizing) {
+ mDragResizing = false;
+ synchronized (mWindowCallbacks) {
+ for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
+ mWindowCallbacks.get(i).onWindowDragResizeEnd();
+ }
+ }
+ mFullRedrawNeeded = true;
+ }
+ }
+
+ /**
* Class for managing the accessibility interaction connection
* based on the global accessibility state.
*/
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 5f4e7af9390e..135448511db3 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -28,9 +28,8 @@ import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.graphics.PixelFormat;
-import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.media.session.MediaController;
import android.net.Uri;
import android.os.Bundle;
@@ -2007,16 +2006,6 @@ public abstract class Window {
*/
public abstract void setNavigationBarColor(@ColorInt int color);
- /**
- * Get information whether the activity has non client decoration view. These views are used in
- * the multi window environment, to provide dragging handle and maximize/close buttons.
- *
- * @hide
- */
- public boolean hasNonClientDecorView() {
- return false;
- }
-
/** @hide */
public void setTheme(int resId) {
}
diff --git a/core/java/android/view/WindowCallbacks.java b/core/java/android/view/WindowCallbacks.java
new file mode 100644
index 000000000000..1aeaec8c6d15
--- /dev/null
+++ b/core/java/android/view/WindowCallbacks.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Rect;
+
+/**
+ * These callbacks are used to communicate window configuration changes while the user is performing
+ * window changes.
+ * Note: Note that at the time of onWindowDragResizeStart the content size isn't known. A consumer
+ * should therfore not draw anything before the additional onContentDraw call has arrived.
+ * @hide
+ */
+public interface WindowCallbacks {
+ /**
+ * Called by the system when the window got changed by the user, before the layouter got called.
+ * It can be used to perform a "quick and dirty" resize which should never take more then 4ms to
+ * complete.
+ *
+ * <p>At the time the layouting has not happened yet.
+ *
+ * @param newBounds The new window frame bounds.
+ */
+ void onWindowSizeIsChanging(Rect newBounds);
+
+ /**
+ * Called when a drag resize starts.
+ * @param initialBounds The initial bounds where the window will be.
+ */
+ void onWindowDragResizeStart(Rect initialBounds);
+
+ /**
+ * Called when a drag resize ends.
+ */
+ void onWindowDragResizeEnd();
+
+ /**
+ * The content will now be drawn to these bounds.
+ */
+ void onContentDraw(int offsetX, int offsetY, int sizeX, int sizeY);
+}
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index a6d99325b094..3882bca043e8 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -263,4 +263,7 @@ public abstract class WindowManagerInternal {
*/
public abstract void setOnHardKeyboardStatusChangeListener(
OnHardKeyboardStatusChangeListener listener);
+
+ /** Returns true if the stack with the input Id is currently visible. */
+ public abstract boolean isStackVisible(int stackId);
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 72971e85dd24..81ec3306c4c7 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -88,6 +88,9 @@ public final class AccessibilityManager {
/** @hide */
public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12;
+ /** @hide */
+ public static final int AUTOCLICK_DELAY_DEFAULT = 600;
+
static final Object sInstanceSync = new Object();
private static AccessibilityManager sInstance;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d0c50c9309c2..358f939c62e2 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.Widget;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -1850,7 +1851,7 @@ public class WebView extends AbsoluteLayout
* <a href="https://html.spec.whatwg.org/multipage/comms.html#messagechannel">here
* </a>
*
- * The returned message channels are entangled and already in started state.
+ * <p>The returned message channels are entangled and already in started state.</p>
*
* @return the two message ports that form the message channel.
*/
@@ -2157,6 +2158,10 @@ public class WebView extends AbsoluteLayout
WebView.super.setLayoutParams(params);
}
+ public void super_startActivityForResult(Intent intent, int requestCode) {
+ WebView.super.startActivityForResult(intent, requestCode);
+ }
+
// ---- Access to non-public methods ----
public void overScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
@@ -2594,6 +2599,23 @@ public class WebView extends AbsoluteLayout
mProvider.getViewDelegate().onFinishTemporaryDetach();
}
+ /**
+ * Receive the result from a previous call to {@link #startActivityForResult(Intent, int)}.
+ *
+ * @param requestCode The integer request code originally supplied to
+ * startActivityForResult(), allowing you to identify who this
+ * result came from.
+ * @param resultCode The integer result code returned by the child activity
+ * through its setResult().
+ * @param data An Intent, which can return result data to the caller
+ * (various data can be attached to Intent "extras").
+ * @hide
+ */
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mProvider.getViewDelegate().onActivityResult(requestCode, resultCode, data);
+ }
+
/** @hide */
@Override
protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 27033ad61186..3ce034cf24cb 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -18,6 +18,7 @@ package android.webkit;
import android.annotation.SystemApi;
import android.content.res.Configuration;
+import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
@@ -380,6 +381,8 @@ public interface WebViewProvider {
public void onStartTemporaryDetach();
public void onFinishTemporaryDetach();
+
+ public void onActivityResult(int requestCode, int resultCode, Intent data);
}
interface ScrollDelegate {
diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java
index 932b3540925b..0e3a69f5f0cd 100644
--- a/core/java/android/widget/AdapterViewAnimator.java
+++ b/core/java/android/widget/AdapterViewAnimator.java
@@ -401,12 +401,11 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
}
LayoutParams createOrReuseLayoutParams(View v) {
- final ViewGroup.LayoutParams currentLp = v.getLayoutParams();
- if (currentLp instanceof ViewGroup.LayoutParams) {
- LayoutParams lp = (LayoutParams) currentLp;
- return lp;
+ final LayoutParams currentLp = v.getLayoutParams();
+ if (currentLp != null) {
+ return currentLp;
}
- return new ViewGroup.LayoutParams(0, 0);
+ return new LayoutParams(0, 0);
}
void refreshChildren() {
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index c40289e44bcf..559181bdb823 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -383,6 +383,7 @@ class FastScroller {
break;
}
}
+ ta.recycle();
updateAppearance();
}
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 95711092a39b..72202567d9bd 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -16,12 +16,7 @@
package android.widget;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.Keyframe;
import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -35,6 +30,7 @@ import android.graphics.Region;
import android.graphics.Typeface;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.FloatProperty;
import android.util.IntArray;
import android.util.Log;
import android.util.MathUtils;
@@ -50,7 +46,6 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.R;
import com.android.internal.widget.ExploreByTouchHelper;
-import java.util.ArrayList;
import java.util.Calendar;
import java.util.Locale;
@@ -60,6 +55,7 @@ import java.util.Locale;
* @hide
*/
public class RadialTimePickerView extends View {
+
private static final String TAG = "RadialTimePickerView";
private static final int HOURS = 0;
@@ -73,12 +69,6 @@ public class RadialTimePickerView extends View {
private static final int AM = 0;
private static final int PM = 1;
- // Opaque alpha level
- private static final int ALPHA_OPAQUE = 255;
-
- // Transparent alpha level
- private static final int ALPHA_TRANSPARENT = 0;
-
private static final int HOURS_IN_CIRCLE = 12;
private static final int MINUTES_IN_CIRCLE = 60;
private static final int DEGREES_FOR_ONE_HOUR = 360 / HOURS_IN_CIRCLE;
@@ -88,8 +78,8 @@ public class RadialTimePickerView extends View {
private static final int[] HOURS_NUMBERS_24 = {0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23};
private static final int[] MINUTES_NUMBERS = {0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55};
- private static final int FADE_OUT_DURATION = 500;
- private static final int FADE_IN_DURATION = 500;
+ private static final int ANIM_DURATION_NORMAL = 500;
+ private static final int ANIM_DURATION_TOUCH = 60;
private static final int[] SNAP_PREFER_30S_MAP = new int[361];
@@ -97,6 +87,9 @@ public class RadialTimePickerView extends View {
private static final float[] COS_30 = new float[NUM_POSITIONS];
private static final float[] SIN_30 = new float[NUM_POSITIONS];
+ /** "Something is wrong" color used when a color attribute is missing. */
+ private static final int MISSING_COLOR = Color.MAGENTA;
+
static {
// Prepare mapping to snap touchable degrees to selectable degrees.
preparePrefer30sMap();
@@ -110,8 +103,19 @@ public class RadialTimePickerView extends View {
}
}
- private final InvalidateUpdateListener mInvalidateUpdateListener =
- new InvalidateUpdateListener();
+ private final FloatProperty<RadialTimePickerView> HOURS_TO_MINUTES =
+ new FloatProperty<RadialTimePickerView>("hoursToMinutes") {
+ @Override
+ public Float get(RadialTimePickerView radialTimePickerView) {
+ return radialTimePickerView.mHoursToMinutes;
+ }
+
+ @Override
+ public void setValue(RadialTimePickerView object, float value) {
+ object.mHoursToMinutes = value;
+ object.invalidate();
+ }
+ };
private final String[] mHours12Texts = new String[12];
private final String[] mOuterHours24Texts = new String[12];
@@ -119,15 +123,8 @@ public class RadialTimePickerView extends View {
private final String[] mMinutesTexts = new String[12];
private final Paint[] mPaint = new Paint[2];
- private final IntHolder[] mAlpha = new IntHolder[2];
-
private final Paint mPaintCenter = new Paint();
-
- private final Paint[][] mPaintSelector = new Paint[2][3];
-
- private final int mSelectorColor;
- private final int mSelectorDotColor;
-
+ private final Paint[] mPaintSelector = new Paint[3];
private final Paint mPaintBackground = new Paint();
private final Typeface mTypeface;
@@ -144,9 +141,6 @@ public class RadialTimePickerView extends View {
private final int[] mSelectionDegrees = new int[2];
- private final ArrayList<Animator> mHoursToMinutesAnims = new ArrayList<>();
- private final ArrayList<Animator> mMinuteToHoursAnims = new ArrayList<>();
-
private final RadialPickerTouchHelper mTouchHelper;
private final Path mSelectorPath = new Path();
@@ -154,6 +148,9 @@ public class RadialTimePickerView extends View {
private boolean mIs24HourMode;
private boolean mShowHours;
+ private ObjectAnimator mHoursToMinutesAnimator;
+ private float mHoursToMinutes;
+
/**
* When in 24-hour mode, indicates that the current hour is between
* 1 and 12 (inclusive).
@@ -165,6 +162,9 @@ public class RadialTimePickerView extends View {
private int mSelectorDotRadius;
private int mCenterDotRadius;
+ private int mSelectorColor;
+ private int mSelectorDotColor;
+
private int mXCenter;
private int mYCenter;
private int mCircleRadius;
@@ -176,7 +176,6 @@ public class RadialTimePickerView extends View {
private String[] mOuterTextHours;
private String[] mInnerTextHours;
private String[] mMinutesText;
- private AnimatorSet mTransition;
private int mAmOrPm;
@@ -304,27 +303,15 @@ public class RadialTimePickerView extends View {
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs);
+ applyAttributes(attrs, defStyleAttr, defStyleRes);
+
// Pull disabled alpha from theme.
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
mDisabledAlpha = outValue.getFloat();
- // process style attributes
- final Resources res = getResources();
- final TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.TimePicker,
- defStyleAttr, defStyleRes);
-
mTypeface = Typeface.create("sans-serif", Typeface.NORMAL);
- // Initialize all alpha values to opaque.
- for (int i = 0; i < mAlpha.length; i++) {
- mAlpha[i] = new IntHolder(ALPHA_OPAQUE);
- }
-
- mTextColor[HOURS] = a.getColorStateList(R.styleable.TimePicker_numbersTextColor);
- mTextColor[HOURS_INNER] = a.getColorStateList(R.styleable.TimePicker_numbersInnerTextColor);
- mTextColor[MINUTES] = mTextColor[HOURS];
-
mPaint[HOURS] = new Paint();
mPaint[HOURS].setAntiAlias(true);
mPaint[HOURS].setTextAlign(Paint.Align.CENTER);
@@ -333,44 +320,21 @@ public class RadialTimePickerView extends View {
mPaint[MINUTES].setAntiAlias(true);
mPaint[MINUTES].setTextAlign(Paint.Align.CENTER);
- final ColorStateList selectorColors = a.getColorStateList(
- R.styleable.TimePicker_numbersSelectorColor);
- final int selectorActivatedColor = selectorColors.getColorForState(
- StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
-
- mPaintCenter.setColor(selectorActivatedColor);
mPaintCenter.setAntiAlias(true);
- final int[] activatedStateSet = StateSet.get(
- StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED);
-
- mSelectorColor = selectorActivatedColor;
- mSelectorDotColor = mTextColor[HOURS].getColorForState(activatedStateSet, 0);
-
- mPaintSelector[HOURS][SELECTOR_CIRCLE] = new Paint();
- mPaintSelector[HOURS][SELECTOR_CIRCLE].setAntiAlias(true);
-
- mPaintSelector[HOURS][SELECTOR_DOT] = new Paint();
- mPaintSelector[HOURS][SELECTOR_DOT].setAntiAlias(true);
-
- mPaintSelector[HOURS][SELECTOR_LINE] = new Paint();
- mPaintSelector[HOURS][SELECTOR_LINE].setAntiAlias(true);
- mPaintSelector[HOURS][SELECTOR_LINE].setStrokeWidth(2);
+ mPaintSelector[SELECTOR_CIRCLE] = new Paint();
+ mPaintSelector[SELECTOR_CIRCLE].setAntiAlias(true);
- mPaintSelector[MINUTES][SELECTOR_CIRCLE] = new Paint();
- mPaintSelector[MINUTES][SELECTOR_CIRCLE].setAntiAlias(true);
+ mPaintSelector[SELECTOR_DOT] = new Paint();
+ mPaintSelector[SELECTOR_DOT].setAntiAlias(true);
- mPaintSelector[MINUTES][SELECTOR_DOT] = new Paint();
- mPaintSelector[MINUTES][SELECTOR_DOT].setAntiAlias(true);
+ mPaintSelector[SELECTOR_LINE] = new Paint();
+ mPaintSelector[SELECTOR_LINE].setAntiAlias(true);
+ mPaintSelector[SELECTOR_LINE].setStrokeWidth(2);
- mPaintSelector[MINUTES][SELECTOR_LINE] = new Paint();
- mPaintSelector[MINUTES][SELECTOR_LINE].setAntiAlias(true);
- mPaintSelector[MINUTES][SELECTOR_LINE].setStrokeWidth(2);
-
- mPaintBackground.setColor(a.getColor(R.styleable.TimePicker_numbersBackgroundColor,
- context.getColor(R.color.timepicker_default_numbers_background_color_material)));
mPaintBackground.setAntiAlias(true);
+ final Resources res = getResources();
mSelectorRadius = res.getDimensionPixelSize(R.dimen.timepicker_selector_radius);
mSelectorStroke = res.getDimensionPixelSize(R.dimen.timepicker_selector_stroke);
mSelectorDotRadius = res.getDimensionPixelSize(R.dimen.timepicker_selector_dot_radius);
@@ -385,6 +349,7 @@ public class RadialTimePickerView extends View {
mTextInset[HOURS_INNER] = res.getDimensionPixelSize(R.dimen.timepicker_text_inset_inner);
mShowHours = true;
+ mHoursToMinutes = HOURS;
mIs24HourMode = false;
mAmOrPm = AM;
@@ -399,8 +364,6 @@ public class RadialTimePickerView extends View {
initHoursAndMinutesText();
initData();
- a.recycle();
-
// Initial values
final Calendar calendar = Calendar.getInstance(Locale.getDefault());
final int currentHour = calendar.get(Calendar.HOUR_OF_DAY);
@@ -412,6 +375,48 @@ public class RadialTimePickerView extends View {
setHapticFeedbackEnabled(true);
}
+ void applyAttributes(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ final Context context = getContext();
+ final TypedArray a = getContext().obtainStyledAttributes(attrs,
+ R.styleable.TimePicker, defStyleAttr, defStyleRes);
+
+ final ColorStateList numbersTextColor = a.getColorStateList(
+ R.styleable.TimePicker_numbersTextColor);
+ final ColorStateList numbersInnerTextColor = a.getColorStateList(
+ R.styleable.TimePicker_numbersInnerTextColor);
+ mTextColor[HOURS] = numbersTextColor == null ?
+ ColorStateList.valueOf(MISSING_COLOR) : numbersTextColor;
+ mTextColor[HOURS_INNER] = numbersInnerTextColor == null ?
+ ColorStateList.valueOf(MISSING_COLOR) : numbersInnerTextColor;
+ mTextColor[MINUTES] = mTextColor[HOURS];
+
+ // Set up various colors derived from the selector "activated" state.
+ final ColorStateList selectorColors = a.getColorStateList(
+ R.styleable.TimePicker_numbersSelectorColor);
+ final int selectorActivatedColor;
+ if (selectorColors != null) {
+ final int[] stateSetEnabledActivated = StateSet.get(
+ StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED);
+ selectorActivatedColor = selectorColors.getColorForState(
+ stateSetEnabledActivated, 0);
+ } else {
+ selectorActivatedColor = MISSING_COLOR;
+ }
+
+ mPaintCenter.setColor(selectorActivatedColor);
+
+ final int[] stateSetActivated = StateSet.get(
+ StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED);
+
+ mSelectorColor = selectorActivatedColor;
+ mSelectorDotColor = mTextColor[HOURS].getColorForState(stateSetActivated, 0);
+
+ mPaintBackground.setColor(a.getColor(R.styleable.TimePicker_numbersBackgroundColor,
+ context.getColor(R.color.timepicker_default_numbers_background_color_material)));
+
+ a.recycle();
+ }
+
public void initialize(int hour, int minute, boolean is24HourMode) {
if (mIs24HourMode != is24HourMode) {
mIs24HourMode = is24HourMode;
@@ -569,35 +574,11 @@ public class RadialTimePickerView extends View {
}
public void showHours(boolean animate) {
- if (mShowHours) {
- return;
- }
-
- mShowHours = true;
-
- if (animate) {
- startMinutesToHoursAnimation();
- }
-
- initData();
- invalidate();
- mTouchHelper.invalidateRoot();
+ showPicker(true, animate);
}
public void showMinutes(boolean animate) {
- if (!mShowHours) {
- return;
- }
-
- mShowHours = false;
-
- if (animate) {
- startHoursToMinutesAnimation();
- }
-
- initData();
- invalidate();
- mTouchHelper.invalidateRoot();
+ showPicker(false, animate);
}
private void initHoursAndMinutesText() {
@@ -620,12 +601,6 @@ public class RadialTimePickerView extends View {
}
mMinutesText = mMinutesTexts;
-
- final int hoursAlpha = mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT;
- mAlpha[HOURS].setValue(hoursAlpha);
-
- final int minutesAlpha = mShowHours ? ALPHA_TRANSPARENT : ALPHA_OPAQUE;
- mAlpha[MINUTES].setValue(minutesAlpha);
}
@Override
@@ -653,90 +628,144 @@ public class RadialTimePickerView extends View {
final float alphaMod = mInputEnabled ? 1 : mDisabledAlpha;
drawCircleBackground(canvas);
- drawHours(canvas, alphaMod);
- drawMinutes(canvas, alphaMod);
+
+ final Path selectorPath = mSelectorPath;
+ drawSelector(canvas, selectorPath);
+ drawHours(canvas, selectorPath, alphaMod);
+ drawMinutes(canvas, selectorPath, alphaMod);
drawCenter(canvas, alphaMod);
}
+ private void showPicker(boolean hours, boolean animate) {
+ if (mShowHours == hours) {
+ return;
+ }
+
+ mShowHours = hours;
+
+ if (animate) {
+ animatePicker(hours, ANIM_DURATION_NORMAL);
+ }
+
+ initData();
+ invalidate();
+ mTouchHelper.invalidateRoot();
+ }
+
+ private void animatePicker(boolean hoursToMinutes, long duration) {
+ final float target = hoursToMinutes ? HOURS : MINUTES;
+ if (mHoursToMinutes == target) {
+ // If we have a pending or running animator, cancel it.
+ if (mHoursToMinutesAnimator != null && mHoursToMinutesAnimator.isStarted()) {
+ mHoursToMinutesAnimator.cancel();
+ mHoursToMinutesAnimator = null;
+ }
+
+ // We're already showing the correct picker.
+ return;
+ }
+
+ mHoursToMinutesAnimator = ObjectAnimator.ofFloat(this, HOURS_TO_MINUTES, target);
+ mHoursToMinutesAnimator.setAutoCancel(true);
+ mHoursToMinutesAnimator.setDuration(duration);
+ mHoursToMinutesAnimator.start();
+ }
+
private void drawCircleBackground(Canvas canvas) {
canvas.drawCircle(mXCenter, mYCenter, mCircleRadius, mPaintBackground);
}
- private void drawHours(Canvas canvas, float alphaMod) {
- final int hoursAlpha = (int) (mAlpha[HOURS].getValue() * alphaMod + 0.5f);
+ private void drawHours(Canvas canvas, Path selectorPath, float alphaMod) {
+ final int hoursAlpha = (int) (255f * (1f - mHoursToMinutes) * alphaMod + 0.5f);
if (hoursAlpha > 0) {
- // Draw the hour selector under the elements.
- drawSelector(canvas, mIsOnInnerCircle ? HOURS_INNER : HOURS, null, alphaMod);
-
- // Draw outer hours.
- drawTextElements(canvas, mTextSize[HOURS], mTypeface, mTextColor[HOURS],
- mOuterTextHours, mOuterTextX[HOURS], mOuterTextY[HOURS], mPaint[HOURS],
- hoursAlpha, !mIsOnInnerCircle, mSelectionDegrees[HOURS], false);
-
- // Draw inner hours (13-00) for 24-hour time.
- if (mIs24HourMode && mInnerTextHours != null) {
- drawTextElements(canvas, mTextSize[HOURS_INNER], mTypeface, mTextColor[HOURS_INNER],
- mInnerTextHours, mInnerTextX, mInnerTextY, mPaint[HOURS], hoursAlpha,
- mIsOnInnerCircle, mSelectionDegrees[HOURS], false);
- }
+ // Exclude the selector region, then draw inner/outer hours with no
+ // activated states.
+ canvas.save(Canvas.CLIP_SAVE_FLAG);
+ canvas.clipPath(selectorPath, Region.Op.DIFFERENCE);
+ drawHoursClipped(canvas, hoursAlpha, false);
+ canvas.restore();
+
+ // Intersect the selector region, then draw minutes with only
+ // activated states.
+ canvas.save(Canvas.CLIP_SAVE_FLAG);
+ canvas.clipPath(selectorPath, Region.Op.INTERSECT);
+ drawHoursClipped(canvas, hoursAlpha, true);
+ canvas.restore();
}
}
- private void drawMinutes(Canvas canvas, float alphaMod) {
- final int minutesAlpha = (int) (mAlpha[MINUTES].getValue() * alphaMod + 0.5f);
- if (minutesAlpha > 0) {
- // Draw the minute selector under the elements.
- drawSelector(canvas, MINUTES, mSelectorPath, alphaMod);
+ private void drawHoursClipped(Canvas canvas, int hoursAlpha, boolean showActivated) {
+ // Draw outer hours.
+ drawTextElements(canvas, mTextSize[HOURS], mTypeface, mTextColor[HOURS], mOuterTextHours,
+ mOuterTextX[HOURS], mOuterTextY[HOURS], mPaint[HOURS], hoursAlpha,
+ showActivated && !mIsOnInnerCircle, mSelectionDegrees[HOURS], showActivated);
+ // Draw inner hours (13-00) for 24-hour time.
+ if (mIs24HourMode && mInnerTextHours != null) {
+ drawTextElements(canvas, mTextSize[HOURS_INNER], mTypeface, mTextColor[HOURS_INNER],
+ mInnerTextHours, mInnerTextX, mInnerTextY, mPaint[HOURS], hoursAlpha,
+ showActivated && mIsOnInnerCircle, mSelectionDegrees[HOURS], showActivated);
+ }
+ }
+
+ private void drawMinutes(Canvas canvas, Path selectorPath, float alphaMod) {
+ final int minutesAlpha = (int) (255f * mHoursToMinutes * alphaMod + 0.5f);
+ if (minutesAlpha > 0) {
// Exclude the selector region, then draw minutes with no
// activated states.
canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipPath(mSelectorPath, Region.Op.DIFFERENCE);
- drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES],
- mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
- minutesAlpha, false, 0, false);
+ canvas.clipPath(selectorPath, Region.Op.DIFFERENCE);
+ drawMinutesClipped(canvas, minutesAlpha, false);
canvas.restore();
// Intersect the selector region, then draw minutes with only
// activated states.
canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipPath(mSelectorPath, Region.Op.INTERSECT);
- drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES],
- mMinutesText, mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES],
- minutesAlpha, true, mSelectionDegrees[MINUTES], true);
+ canvas.clipPath(selectorPath, Region.Op.INTERSECT);
+ drawMinutesClipped(canvas, minutesAlpha, true);
canvas.restore();
}
}
+ private void drawMinutesClipped(Canvas canvas, int minutesAlpha, boolean showActivated) {
+ drawTextElements(canvas, mTextSize[MINUTES], mTypeface, mTextColor[MINUTES], mMinutesText,
+ mOuterTextX[MINUTES], mOuterTextY[MINUTES], mPaint[MINUTES], minutesAlpha,
+ showActivated, mSelectionDegrees[MINUTES], showActivated);
+ }
+
private void drawCenter(Canvas canvas, float alphaMod) {
mPaintCenter.setAlpha((int) (255 * alphaMod + 0.5f));
canvas.drawCircle(mXCenter, mYCenter, mCenterDotRadius, mPaintCenter);
}
- private int applyAlpha(int argb, int alpha) {
- final int srcAlpha = (argb >> 24) & 0xFF;
- final int dstAlpha = (int) (srcAlpha * (alpha / 255.0) + 0.5f);
- return (0xFFFFFF & argb) | (dstAlpha << 24);
- }
-
private int getMultipliedAlpha(int argb, int alpha) {
return (int) (Color.alpha(argb) * (alpha / 255.0) + 0.5);
}
- private void drawSelector(Canvas canvas, int index, Path selectorPath, float alphaMod) {
- final int alpha = (int) (mAlpha[index % 2].getValue() * alphaMod + 0.5f);
- final int color = applyAlpha(mSelectorColor, alpha);
+ private void drawSelector(Canvas canvas, Path selectorPath) {
+ // Determine the current length, angle, and dot scaling factor.
+ final int hoursIndex = mIsOnInnerCircle ? HOURS_INNER : HOURS;
+ final int hoursInset = mTextInset[hoursIndex];
+ final int hoursAngleDeg = mSelectionDegrees[hoursIndex % 2];
+ final float hoursDotScale = mSelectionDegrees[hoursIndex % 2] % 30 != 0 ? 1 : 0;
+
+ final int minutesIndex = MINUTES;
+ final int minutesInset = mTextInset[minutesIndex];
+ final int minutesAngleDeg = mSelectionDegrees[minutesIndex];
+ final float minutesDotScale = mSelectionDegrees[minutesIndex] % 30 != 0 ? 1 : 0;
// Calculate the current radius at which to place the selection circle.
final int selRadius = mSelectorRadius;
- final int selLength = mCircleRadius - mTextInset[index];
- final double selAngleRad = Math.toRadians(mSelectionDegrees[index % 2]);
+ final float selLength =
+ mCircleRadius - MathUtils.lerp(hoursInset, minutesInset, mHoursToMinutes);
+ final double selAngleRad =
+ Math.toRadians(MathUtils.lerpDeg(hoursAngleDeg, minutesAngleDeg, mHoursToMinutes));
final float selCenterX = mXCenter + selLength * (float) Math.sin(selAngleRad);
final float selCenterY = mYCenter - selLength * (float) Math.cos(selAngleRad);
// Draw the selection circle.
- final Paint paint = mPaintSelector[index % 2][SELECTOR_CIRCLE];
- paint.setColor(color);
+ final Paint paint = mPaintSelector[SELECTOR_CIRCLE];
+ paint.setColor(mSelectorColor);
canvas.drawCircle(selCenterX, selCenterY, selRadius, paint);
// If needed, set up the clip path for later.
@@ -746,26 +775,26 @@ public class RadialTimePickerView extends View {
}
// Draw the dot if we're between two items.
- final boolean shouldDrawDot = mSelectionDegrees[index % 2] % 30 != 0;
- if (shouldDrawDot) {
- final Paint dotPaint = mPaintSelector[index % 2][SELECTOR_DOT];
+ final float dotScale = MathUtils.lerp(hoursDotScale, minutesDotScale, mHoursToMinutes);
+ if (dotScale > 0) {
+ final Paint dotPaint = mPaintSelector[SELECTOR_DOT];
dotPaint.setColor(mSelectorDotColor);
- canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius, dotPaint);
+ canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius * dotScale, dotPaint);
}
// Shorten the line to only go from the edge of the center dot to the
// edge of the selection circle.
final double sin = Math.sin(selAngleRad);
final double cos = Math.cos(selAngleRad);
- final int lineLength = selLength - selRadius;
+ final float lineLength = selLength - selRadius;
final int centerX = mXCenter + (int) (mCenterDotRadius * sin);
final int centerY = mYCenter - (int) (mCenterDotRadius * cos);
final float linePointX = centerX + (int) (lineLength * sin);
final float linePointY = centerY - (int) (lineLength * cos);
// Draw the line.
- final Paint linePaint = mPaintSelector[index % 2][SELECTOR_LINE];
- linePaint.setColor(color);
+ final Paint linePaint = mPaintSelector[SELECTOR_LINE];
+ linePaint.setColor(mSelectorColor);
linePaint.setStrokeWidth(mSelectorStroke);
canvas.drawLine(mXCenter, mYCenter, linePointX, linePointY, linePaint);
}
@@ -842,73 +871,6 @@ public class RadialTimePickerView extends View {
}
}
- private static ObjectAnimator getFadeOutAnimator(IntHolder target, int startAlpha, int endAlpha,
- InvalidateUpdateListener updateListener) {
- final ObjectAnimator animator = ObjectAnimator.ofInt(target, "value", startAlpha, endAlpha);
- animator.setDuration(FADE_OUT_DURATION);
- animator.addUpdateListener(updateListener);
- return animator;
- }
-
- private static ObjectAnimator getFadeInAnimator(IntHolder target, int startAlpha, int endAlpha,
- InvalidateUpdateListener updateListener) {
- final float delayMultiplier = 0.25f;
- final float transitionDurationMultiplier = 1f;
- final float totalDurationMultiplier = transitionDurationMultiplier + delayMultiplier;
- final int totalDuration = (int) (FADE_IN_DURATION * totalDurationMultiplier);
- final float delayPoint = (delayMultiplier * FADE_IN_DURATION) / totalDuration;
-
- final Keyframe kf0, kf1, kf2;
- kf0 = Keyframe.ofInt(0f, startAlpha);
- kf1 = Keyframe.ofInt(delayPoint, startAlpha);
- kf2 = Keyframe.ofInt(1f, endAlpha);
- final PropertyValuesHolder fadeIn = PropertyValuesHolder.ofKeyframe("value", kf0, kf1, kf2);
-
- final ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(target, fadeIn);
- animator.setDuration(totalDuration);
- animator.addUpdateListener(updateListener);
- return animator;
- }
-
- private class InvalidateUpdateListener implements ValueAnimator.AnimatorUpdateListener {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- RadialTimePickerView.this.invalidate();
- }
- }
-
- private void startHoursToMinutesAnimation() {
- if (mHoursToMinutesAnims.size() == 0) {
- mHoursToMinutesAnims.add(getFadeOutAnimator(mAlpha[HOURS],
- ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mHoursToMinutesAnims.add(getFadeInAnimator(mAlpha[MINUTES],
- ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- }
-
- if (mTransition != null && mTransition.isRunning()) {
- mTransition.end();
- }
- mTransition = new AnimatorSet();
- mTransition.playTogether(mHoursToMinutesAnims);
- mTransition.start();
- }
-
- private void startMinutesToHoursAnimation() {
- if (mMinuteToHoursAnims.size() == 0) {
- mMinuteToHoursAnims.add(getFadeOutAnimator(mAlpha[MINUTES],
- ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
- mMinuteToHoursAnims.add(getFadeInAnimator(mAlpha[HOURS],
- ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
- }
-
- if (mTransition != null && mTransition.isRunning()) {
- mTransition.end();
- }
- mTransition = new AnimatorSet();
- mTransition.playTogether(mMinuteToHoursAnims);
- mTransition.start();
- }
-
private int getDegreesFromXY(float x, float y, boolean constrainOutside) {
// Ensure the point is inside the touchable area.
final int innerBound;
@@ -992,6 +954,9 @@ public class RadialTimePickerView extends View {
return false;
}
+ // Ensure we're showing the correct picker.
+ animatePicker(mShowHours, ANIM_DURATION_TOUCH);
+
final int type;
final int newValue;
final boolean valueChanged;
@@ -1356,20 +1321,4 @@ public class RadialTimePickerView extends View {
return id >>> SHIFT_VALUE & MASK_VALUE;
}
}
-
- private static class IntHolder {
- private int mValue;
-
- public IntHolder(int value) {
- mValue = value;
- }
-
- public void setValue(int value) {
- mValue = value;
- }
-
- public int getValue() {
- return mValue;
- }
- }
}
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 61ef6dc9d231..6d41c1d34058 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -200,8 +200,8 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl
a.recycle();
- mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(
- R.id.radial_picker);
+ mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(R.id.radial_picker);
+ mRadialTimePickerView.applyAttributes(attrs, defStyleAttr, defStyleRes);
setupListeners();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index f0e216fc7873..255e23a56a30 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -16,6 +16,8 @@
package com.android.internal.app;
+import android.animation.ObjectAnimator;
+import android.annotation.NonNull;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
@@ -29,6 +31,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.database.DataSetObserver;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Bundle;
@@ -46,13 +49,18 @@ import android.service.chooser.ChooserTargetService;
import android.service.chooser.IChooserTargetResult;
import android.service.chooser.IChooserTargetService;
import android.text.TextUtils;
+import android.util.FloatProperty;
import android.util.Log;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.MeasureSpec;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ListView;
@@ -80,6 +88,7 @@ public class ChooserActivity extends ResolverActivity {
private Intent mReferrerFillInIntent;
private ChooserListAdapter mChooserListAdapter;
+ private ChooserRowAdapter mChooserRowAdapter;
private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>();
@@ -253,7 +262,9 @@ public class ChooserActivity extends ResolverActivity {
boolean alwaysUseOption) {
final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
mChooserListAdapter = (ChooserListAdapter) adapter;
- adapterView.setAdapter(new ChooserRowAdapter(mChooserListAdapter));
+ mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter);
+ mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView));
+ adapterView.setAdapter(mChooserRowAdapter);
if (listView != null) {
listView.setItemsCanFocus(true);
}
@@ -913,19 +924,103 @@ public class ChooserActivity extends ResolverActivity {
}
}
+ static class RowScale {
+ private static final int DURATION = 400;
+
+ float mScale;
+ ChooserRowAdapter mAdapter;
+ private final ObjectAnimator mAnimator;
+
+ public static final FloatProperty<RowScale> PROPERTY =
+ new FloatProperty<RowScale>("scale") {
+ @Override
+ public void setValue(RowScale object, float value) {
+ object.mScale = value;
+ object.mAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public Float get(RowScale object) {
+ return object.mScale;
+ }
+ };
+
+ public RowScale(@NonNull ChooserRowAdapter adapter, float from, float to) {
+ mAdapter = adapter;
+ mScale = from;
+ if (from == to) {
+ mAnimator = null;
+ return;
+ }
+
+ mAnimator = ObjectAnimator.ofFloat(this, PROPERTY, from, to).setDuration(DURATION);
+ }
+
+ public RowScale setInterpolator(Interpolator interpolator) {
+ if (mAnimator != null) {
+ mAnimator.setInterpolator(interpolator);
+ }
+ return this;
+ }
+
+ public float get() {
+ return mScale;
+ }
+
+ public void startAnimation() {
+ if (mAnimator != null) {
+ mAnimator.start();
+ }
+ }
+
+ public void cancelAnimation() {
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ }
+ }
+
class ChooserRowAdapter extends BaseAdapter {
private ChooserListAdapter mChooserListAdapter;
private final LayoutInflater mLayoutInflater;
private final int mColumnCount = 4;
+ private RowScale[] mServiceTargetScale;
+ private final Interpolator mInterpolator;
public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) {
mChooserListAdapter = wrappedAdapter;
mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
+ mInterpolator = AnimationUtils.loadInterpolator(ChooserActivity.this,
+ android.R.interpolator.decelerate_quint);
+
wrappedAdapter.registerDataSetObserver(new DataSetObserver() {
@Override
public void onChanged() {
super.onChanged();
+ final int rcount = getServiceTargetRowCount();
+ if (mServiceTargetScale == null
+ || mServiceTargetScale.length != rcount) {
+ RowScale[] old = mServiceTargetScale;
+ int oldRCount = old != null ? old.length : 0;
+ mServiceTargetScale = new RowScale[rcount];
+ if (old != null && rcount > 0) {
+ System.arraycopy(old, 0, mServiceTargetScale, 0,
+ Math.min(old.length, rcount));
+ }
+
+ for (int i = rcount; i < oldRCount; i++) {
+ old[i].cancelAnimation();
+ }
+
+ for (int i = oldRCount; i < rcount; i++) {
+ final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f)
+ .setInterpolator(mInterpolator);
+ mServiceTargetScale[i] = rs;
+ rs.startAnimation();
+ }
+ }
+
notifyDataSetChanged();
}
@@ -933,19 +1028,43 @@ public class ChooserActivity extends ResolverActivity {
public void onInvalidated() {
super.onInvalidated();
notifyDataSetInvalidated();
+ if (mServiceTargetScale != null) {
+ for (RowScale rs : mServiceTargetScale) {
+ rs.cancelAnimation();
+ }
+ }
}
});
}
+ private float getRowScale(int rowPosition) {
+ final int start = getCallerTargetRowCount();
+ final int end = start + getServiceTargetRowCount();
+ if (rowPosition >= start && rowPosition < end) {
+ return mServiceTargetScale[rowPosition - start].get();
+ }
+ return 1.f;
+ }
+
@Override
public int getCount() {
return (int) (
- Math.ceil((float) mChooserListAdapter.getCallerTargetCount() / mColumnCount)
- + Math.ceil((float) mChooserListAdapter.getServiceTargetCount() / mColumnCount)
+ getCallerTargetRowCount()
+ + getServiceTargetRowCount()
+ Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount)
);
}
+ public int getCallerTargetRowCount() {
+ return (int) Math.ceil(
+ (float) mChooserListAdapter.getCallerTargetCount() / mColumnCount);
+ }
+
+ public int getServiceTargetRowCount() {
+ return (int) Math.ceil(
+ (float) mChooserListAdapter.getServiceTargetCount() / mColumnCount);
+ }
+
@Override
public Object getItem(int position) {
// We have nothing useful to return here.
@@ -959,33 +1078,67 @@ public class ChooserActivity extends ResolverActivity {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- final View[] holder;
+ final RowViewHolder holder;
if (convertView == null) {
holder = createViewHolder(parent);
} else {
- holder = (View[]) convertView.getTag();
+ holder = (RowViewHolder) convertView.getTag();
}
bindViewHolder(position, holder);
- // We keep the actual list item view as the last item in the holder array
- return holder[mColumnCount];
+ return holder.row;
}
- View[] createViewHolder(ViewGroup parent) {
- final View[] holder = new View[mColumnCount + 1];
-
+ RowViewHolder createViewHolder(ViewGroup parent) {
final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row,
parent, false);
+ final RowViewHolder holder = new RowViewHolder(row, mColumnCount);
+ final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+
for (int i = 0; i < mColumnCount; i++) {
- holder[i] = mChooserListAdapter.createView(row);
- row.addView(holder[i]);
+ final View v = mChooserListAdapter.createView(row);
+ v.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startSelected(holder.itemIndex, false, true);
+ }
+ });
+ v.setOnLongClickListener(new OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ showAppDetails(
+ mChooserListAdapter.resolveInfoForPosition(holder.itemIndex, true));
+ return true;
+ }
+ });
+ row.addView(v);
+ holder.cells[i] = v;
+
+ // Force height to be a given so we don't have visual disruption during scaling.
+ LayoutParams lp = v.getLayoutParams();
+ v.measure(spec, spec);
+ if (lp == null) {
+ lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight());
+ row.setLayoutParams(lp);
+ } else {
+ lp.height = v.getMeasuredHeight();
+ }
+ }
+
+ // Pre-measure so we can scale later.
+ holder.measure();
+ LayoutParams lp = row.getLayoutParams();
+ if (lp == null) {
+ lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.measuredRowHeight);
+ row.setLayoutParams(lp);
+ } else {
+ lp.height = holder.measuredRowHeight;
}
row.setTag(holder);
- holder[mColumnCount] = row;
return holder;
}
- void bindViewHolder(int rowPosition, View[] holder) {
+ void bindViewHolder(int rowPosition, RowViewHolder holder) {
final int start = getFirstRowPosition(rowPosition);
final int startType = mChooserListAdapter.getPositionTargetType(start);
@@ -994,34 +1147,26 @@ public class ChooserActivity extends ResolverActivity {
end--;
}
- final ViewGroup row = (ViewGroup) holder[mColumnCount];
-
if (startType == ChooserListAdapter.TARGET_SERVICE) {
- row.setBackgroundColor(getColor(R.color.chooser_service_row_background_color));
+ holder.row.setBackgroundColor(
+ getColor(R.color.chooser_service_row_background_color));
} else {
- row.setBackground(null);
+ holder.row.setBackgroundColor(Color.TRANSPARENT);
+ }
+
+ final int oldHeight = holder.row.getLayoutParams().height;
+ holder.row.getLayoutParams().height = Math.max(1,
+ (int) (holder.measuredRowHeight * getRowScale(rowPosition)));
+ if (holder.row.getLayoutParams().height != oldHeight) {
+ holder.row.requestLayout();
}
for (int i = 0; i < mColumnCount; i++) {
- final View v = holder[i];
+ final View v = holder.cells[i];
if (start + i <= end) {
v.setVisibility(View.VISIBLE);
- final int itemIndex = start + i;
- mChooserListAdapter.bindView(itemIndex, v);
- v.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- startSelected(itemIndex, false, true);
- }
- });
- v.setOnLongClickListener(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- showAppDetails(
- mChooserListAdapter.resolveInfoForPosition(itemIndex, true));
- return true;
- }
- });
+ holder.itemIndex = start + i;
+ mChooserListAdapter.bindView(holder.itemIndex, v);
} else {
v.setVisibility(View.GONE);
}
@@ -1048,6 +1193,24 @@ public class ChooserActivity extends ResolverActivity {
}
}
+ static class RowViewHolder {
+ final View[] cells;
+ final ViewGroup row;
+ int measuredRowHeight;
+ int itemIndex;
+
+ public RowViewHolder(ViewGroup row, int cellCount) {
+ this.row = row;
+ this.cells = new View[cellCount];
+ }
+
+ public void measure() {
+ final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ row.measure(spec, spec);
+ measuredRowHeight = row.getMeasuredHeight();
+ }
+ }
+
static class ChooserTargetServiceConnection implements ServiceConnection {
private final DisplayResolveInfo mOriginalTarget;
private ComponentName mConnectedComponent;
@@ -1199,4 +1362,44 @@ public class ChooserActivity extends ResolverActivity {
mSelectedTarget = null;
}
}
+
+ class OffsetDataSetObserver extends DataSetObserver {
+ private final AbsListView mListView;
+ private int mCachedViewType = -1;
+ private View mCachedView;
+
+ public OffsetDataSetObserver(AbsListView listView) {
+ mListView = listView;
+ }
+
+ @Override
+ public void onChanged() {
+ if (mResolverDrawerLayout == null) {
+ return;
+ }
+
+ final int chooserTargetRows = mChooserRowAdapter.getServiceTargetRowCount();
+ int offset = 0;
+ for (int i = 0; i < chooserTargetRows; i++) {
+ final int pos = mChooserRowAdapter.getCallerTargetRowCount() + i;
+ final int vt = mChooserRowAdapter.getItemViewType(pos);
+ if (vt != mCachedViewType) {
+ mCachedView = null;
+ }
+ final View v = mChooserRowAdapter.getView(pos, mCachedView, mListView);
+ int height = ((RowViewHolder) (v.getTag())).measuredRowHeight;
+
+ offset += (int) (height * mChooserRowAdapter.getRowScale(pos) * chooserTargetRows);
+
+ if (vt >= 0) {
+ mCachedViewType = vt;
+ mCachedView = v;
+ } else {
+ mCachedViewType = -1;
+ }
+ }
+
+ mResolverDrawerLayout.setCollapsibleHeightReserved(offset);
+ }
+ }
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 17104894310e..ba0912a5536a 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -103,6 +103,8 @@ public class ResolverActivity extends Activity {
private ResolverComparator mResolverComparator;
private PickTargetOptionRequest mPickOptionRequest;
+ protected ResolverDrawerLayout mResolverDrawerLayout;
+
private boolean mRegistered;
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override public void onSomePackagesChanged() {
@@ -253,6 +255,7 @@ public class ResolverActivity extends Activity {
if (isVoiceInteraction()) {
rdl.setCollapsed(false);
}
+ mResolverDrawerLayout = rdl;
}
if (title == null) {
@@ -1570,7 +1573,10 @@ public class ResolverActivity extends Activity {
private void onBindView(View view, TargetInfo info) {
final ViewHolder holder = (ViewHolder) view.getTag();
- holder.text.setText(info.getDisplayLabel());
+ final CharSequence label = info.getDisplayLabel();
+ if (!TextUtils.equals(holder.text.getText(), label)) {
+ holder.text.setText(info.getDisplayLabel());
+ }
if (showsExtendedInfo(info)) {
holder.text2.setVisibility(View.VISIBLE);
holder.text2.setText(info.getExtendedInfo());
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index b6d736465e17..3ff12adbfa06 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -488,8 +488,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
}
public void clearContentView() {
- if (mNonClientDecorView.getChildCount() > 1) {
- mNonClientDecorView.removeViewAt(1);
+ if (mNonClientDecorView != null) {
+ if (mNonClientDecorView.getChildCount() > 1) {
+ mNonClientDecorView.removeViewAt(1);
+ }
+ } else {
+ // This window doesn't have non client decor, so we need to just remove the children
+ // of the decor view.
+ mDecor.removeAllViews();
}
}
@@ -2615,7 +2621,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (action == MotionEvent.ACTION_DOWN) {
int y = (int)event.getY();
if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
- Log.i(TAG, "Watchiing!");
+ Log.i(TAG, "Watching!");
mWatchingForMenu = true;
}
return false;
@@ -5413,16 +5419,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
* @Return Returns true if the window should show a shadow.
**/
private boolean nonClientDecorHasShadow(int workspaceId) {
- // TODO(skuhne): Add side by side mode here to add a decor.
return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
}
@Override
- public boolean hasNonClientDecorView() {
- return mNonClientDecorView != null;
- }
-
- @Override
public void setTheme(int resid) {
mTheme = resid;
if (mDecor != null) {
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 3eeabcddb3c7..07bfce727964 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -100,14 +100,6 @@ public class BaseIWindow extends IWindow.Stub {
}
@Override
- public void onAnimationStarted(int remainingFrameCount) {
- }
-
- @Override
- public void onAnimationStopped() {
- }
-
- @Override
public void dispatchWindowShown() {
}
}
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 56cf9216d0c9..da43723a028f 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -17,15 +17,22 @@
package com.android.internal.widget;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Rect;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.AttributeSet;
+import android.view.Choreographer;
+import android.view.DisplayListCanvas;
import android.view.MotionEvent;
+import android.view.RenderNode;
+import android.view.ThreadedRenderer;
import android.view.View;
import android.widget.LinearLayout;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.Window;
+import android.view.WindowCallbacks;
import android.util.Log;
import android.util.TypedValue;
@@ -58,7 +65,7 @@ import com.android.internal.policy.PhoneWindow;
* This will be mitigated once b/22527834 will be addressed.
*/
public class NonClientDecorView extends LinearLayout
- implements View.OnClickListener, View.OnTouchListener {
+ implements View.OnClickListener, View.OnTouchListener, WindowCallbacks {
private final static String TAG = "NonClientDecorView";
// The height of a window which has focus in DIP.
private final int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
@@ -67,6 +74,8 @@ public class NonClientDecorView extends LinearLayout
private PhoneWindow mOwner = null;
private boolean mWindowHasShadow = false;
private boolean mShowDecor = false;
+ // True when this object is listening for window size changes.
+ private boolean mAttachedCallbacksToRootViewImpl = false;
// True if the window is being dragged.
private boolean mDragging = false;
@@ -85,6 +94,9 @@ public class NonClientDecorView extends LinearLayout
// to max until the first layout command has been executed.
private boolean mAllowUpdateElevation = false;
+ // The resize frame renderer.
+ private ResizeFrameThread mFrameRendererThread = null;
+
public NonClientDecorView(Context context) {
super(context);
}
@@ -97,6 +109,30 @@ public class NonClientDecorView extends LinearLayout
super(context, attrs, defStyle);
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (!mAttachedCallbacksToRootViewImpl) {
+ // If there is no window callback installed there was no window set before. Set it now.
+ // Note that our ViewRootImpl object will not change.
+ getViewRootImpl().addWindowCallbacks(this);
+ mAttachedCallbacksToRootViewImpl = true;
+ } else if (mFrameRendererThread != null) {
+ // We are resizing and this call happened due to a configuration change. Tell the
+ // renderer about it.
+ mFrameRendererThread.onConfigurationChange();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mAttachedCallbacksToRootViewImpl) {
+ getViewRootImpl().removeWindowCallbacks(this);
+ mAttachedCallbacksToRootViewImpl = false;
+ }
+ }
+
public void setPhoneWindow(PhoneWindow owner, boolean showDecor, boolean windowHasShadow) {
mOwner = owner;
mWindowHasShadow = windowHasShadow;
@@ -108,6 +144,7 @@ public class NonClientDecorView extends LinearLayout
// By changing the outline provider to BOUNDS, the window can remove its
// background without removing the shadow.
mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS);
+
findViewById(R.id.maximize_window).setOnClickListener(this);
findViewById(R.id.close_window).setOnClickListener(this);
}
@@ -251,7 +288,9 @@ public class NonClientDecorView extends LinearLayout
**/
private void updateElevation() {
float elevation = 0;
- if (mWindowHasShadow) {
+ // Do not use a shadow when we are in resizing mode (mRenderer not null) since the shadow
+ // is bound to the content size and not the target size.
+ if (mWindowHasShadow && mFrameRendererThread == null) {
boolean fill = isFillingScreen();
elevation = fill ? 0 :
(mWindowHasFocus ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP :
@@ -293,4 +332,300 @@ public class NonClientDecorView extends LinearLayout
}
}
}
+
+ @Override
+ public void onWindowDragResizeStart(Rect initialBounds) {
+ if (mOwner.isDestroyed()) {
+ // If the owner's window is gone, we should not be able to come here anymore.
+ releaseResources();
+ return;
+ }
+ if (mFrameRendererThread != null) {
+ return;
+ }
+ final ThreadedRenderer renderer =
+ (ThreadedRenderer) mOwner.getDecorView().getHardwareRenderer();
+ if (renderer != null) {
+ mFrameRendererThread = new ResizeFrameThread(renderer, initialBounds);
+ // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
+ // If we want to get the shadow shown while resizing, we would need to elevate a new
+ // element which owns the caption and has the elevation.
+ updateElevation();
+ }
+ }
+
+ @Override
+ public void onContentDraw(int xOffset, int yOffset, int xSize, int ySize) {
+ if (mFrameRendererThread != null) {
+ mFrameRendererThread.onContentDraw(xOffset, yOffset, xSize, ySize);
+ }
+ }
+
+ @Override
+ public void onWindowDragResizeEnd() {
+ releaseThreadedRenderer();
+ }
+
+ @Override
+ public void onWindowSizeIsChanging(Rect newBounds) {
+ if (mFrameRendererThread != null) {
+ mFrameRendererThread.setTargetRect(newBounds);
+ }
+ }
+
+ /**
+ * Release the renderer thread which is usually done when the user stops resizing.
+ */
+ private void releaseThreadedRenderer() {
+ if (mFrameRendererThread != null) {
+ mFrameRendererThread.releaseRenderer();
+ mFrameRendererThread = null;
+ // Bring the shadow back.
+ updateElevation();
+ }
+ }
+
+ /**
+ * Called when the parent window is destroyed to release all resources. Note that this will also
+ * destroy the renderer thread.
+ */
+ private void releaseResources() {
+ releaseThreadedRenderer();
+ }
+
+ /**
+ * The thread which draws the chrome while we are resizing.
+ * It starts with the creation and it ends once someone calls destroy().
+ * Any size changes can be passed by a call to setTargetRect will passed to the thread and
+ * executed via the Choreographer.
+ */
+ private class ResizeFrameThread extends Thread implements Choreographer.FrameCallback {
+ // This is containing the last requested size by a resize command. Note that this size might
+ // or might not have been applied to the output already.
+ private final Rect mTargetRect = new Rect();
+
+ // The render nodes for the multi threaded renderer.
+ private ThreadedRenderer mRenderer;
+ private RenderNode mFrameNode;
+ private RenderNode mBackdropNode;
+
+ private final Rect mOldTargetRect = new Rect();
+ private final Rect mNewTargetRect = new Rect();
+ private Choreographer mChoreographer;
+
+ // Cached size values from the last render for the case that the view hierarchy is gone
+ // during a configuration change.
+ private int mLastContentWidth;
+ private int mLastContentHeight;
+ private int mLastCaptionHeight;
+ private int mLastXOffset;
+ private int mLastYOffset;
+
+ ResizeFrameThread(ThreadedRenderer renderer, Rect initialBounds) {
+ setName("ResizeFrame");
+ mRenderer = renderer;
+
+ // Create the render nodes for our frame and backdrop which can be resized independently
+ // from the content.
+ mFrameNode = RenderNode.create("FrameNode", null);
+ mBackdropNode = RenderNode.create("BackdropNode", null);
+
+ mRenderer.addRenderNode(mFrameNode, false);
+ mRenderer.addRenderNode(mBackdropNode, true);
+
+ // Set the initial bounds and draw once so that we do not get a broken frame.
+ mTargetRect.set(initialBounds);
+ synchronized (this) {
+ changeWindowSizeLocked(initialBounds);
+ }
+
+ // Kick off our draw thread.
+ start();
+ }
+
+ /**
+ * Call this function asynchronously when the window size has been changed. The change will
+ * be picked up once per frame and the frame will be re-rendered accordingly.
+ * @param newTargetBounds The new target bounds.
+ */
+ public void setTargetRect(Rect newTargetBounds) {
+ synchronized (this) {
+ mTargetRect.set(newTargetBounds);
+ // Notify of a bounds change.
+ pingRenderLocked();
+ }
+ }
+
+ /**
+ * The window got replaced due to a configuration change.
+ */
+ public void onConfigurationChange() {
+ synchronized (this) {
+ if (mRenderer != null) {
+ // Enforce a window redraw.
+ mOldTargetRect.set(0, 0, 0, 0);
+ pingRenderLocked();
+ }
+ }
+ }
+
+ /**
+ * All resources of the renderer will be released. This function can be called from the
+ * the UI thread as well as the renderer thread.
+ */
+ public void releaseRenderer() {
+ synchronized (this) {
+ if (mRenderer != null) {
+ // Invalidate the current content bounds.
+ mRenderer.setContentDrawBounds(0, 0, 0, 0);
+
+ // Remove the render nodes again
+ // (see comment above - better to do that only once).
+ mRenderer.removeRenderNode(mFrameNode);
+ mRenderer.removeRenderNode(mBackdropNode);
+
+ mRenderer = null;
+
+ // Exit the renderer loop.
+ pingRenderLocked();
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ try {
+ Looper.prepare();
+ mChoreographer = Choreographer.getInstance();
+ Looper.loop();
+ } finally {
+ releaseRenderer();
+ }
+ synchronized (this) {
+ // Make sure no more messages are being sent.
+ mChoreographer = null;
+ }
+ }
+
+ /**
+ * The implementation of the FrameCallback.
+ * @param frameTimeNanos The time in nanoseconds when the frame started being rendered,
+ * in the {@link System#nanoTime()} timebase. Divide this value by {@code 1000000}
+ */
+ @Override
+ public void doFrame(long frameTimeNanos) {
+ synchronized (this) {
+ if (mRenderer == null) {
+ // Tell the looper to stop. We are done.
+ Looper.myLooper().quit();
+ return;
+ }
+ mNewTargetRect.set(mTargetRect);
+ if (!mNewTargetRect.equals(mOldTargetRect)) {
+ mOldTargetRect.set(mNewTargetRect);
+ changeWindowSizeLocked(mNewTargetRect);
+ }
+ }
+ }
+
+ /**
+ * The content is about to be drawn and we got the location of where it will be shown.
+ * If a "changeWindowSizeLocked" call has already been processed, we will re-issue the call
+ * if the previous call was ignored since the size was unknown.
+ * @param xOffset The x offset where the content is drawn to.
+ * @param yOffset The y offset where the content is drawn to.
+ * @param xSize The width size of the content. This should not be 0.
+ * @param ySize The height of the content.
+ */
+ public void onContentDraw(int xOffset, int yOffset, int xSize, int ySize) {
+ synchronized (this) {
+ final boolean firstCall = mLastContentWidth == 0;
+ // The current content buffer is drawn here.
+ mLastContentWidth = xSize;
+ mLastContentHeight = ySize - mLastCaptionHeight;
+ mLastXOffset = xOffset;
+ mLastYOffset = yOffset;
+
+ mRenderer.setContentDrawBounds(
+ mLastXOffset,
+ mLastYOffset + mLastCaptionHeight,
+ mLastXOffset + mLastContentWidth,
+ mLastYOffset + mLastCaptionHeight + mLastContentHeight);
+ // If this was the first call and changeWindowSizeLocked got already called prior
+ // to us, we should re-issue a changeWindowSizeLocked now.
+ if (firstCall && (mLastCaptionHeight != 0 || !mShowDecor)) {
+ mOldTargetRect.set(0, 0, 0, 0);
+ pingRenderLocked();
+ }
+ }
+ }
+
+ /**
+ * Resizing the frame to fit the new window size.
+ * @param newBounds The window bounds which needs to be drawn.
+ */
+ private void changeWindowSizeLocked(Rect newBounds) {
+ // While a configuration change is taking place the view hierarchy might become
+ // inaccessible. For that case we remember the previous metrics to avoid flashes.
+ // Note that even when there is no visible caption, the caption child will exist.
+ View caption = getChildAt(0);
+ if (caption != null) {
+ final int captionHeight = caption.getHeight();
+ // The caption height will probably never dynamically change while we are resizing.
+ // Once set to something other then 0 it should be kept that way.
+ if (captionHeight != 0) {
+ // Remember the height of the caption.
+ mLastCaptionHeight = captionHeight;
+ }
+ }
+ // Make sure that the other thread has already prepared the render draw calls for the
+ // content. If any size is 0, we have to wait for it to be drawn first.
+ if ((mLastCaptionHeight == 0 && mShowDecor) ||
+ mLastContentWidth == 0 || mLastContentHeight == 0) {
+ return;
+ }
+ // Since the surface is spanning the entire screen, we have to add the start offset of
+ // the bounds to get to the surface location.
+ final int left = mLastXOffset + newBounds.left;
+ final int top = mLastYOffset + newBounds.top;
+ final int width = newBounds.width();
+ final int height = newBounds.height();
+
+ // Produce the draw calls.
+ // TODO(skuhne): Create a separate caption view which draws this. If the shadow should
+ // be resized while the window resizes, this hierarchy needs to have the elevation.
+ // That said - it is probably no good idea to draw the shadow every time since it costs
+ // a considerable time which we should rather spend for resizing the content and it does
+ // barely show while the entire screen is moving.
+ mFrameNode.setLeftTopRightBottom(left, top, left + width, top + mLastCaptionHeight);
+ DisplayListCanvas canvas = mFrameNode.start(width, height);
+ canvas.drawColor(Color.BLACK);
+ mFrameNode.end(canvas);
+
+ mBackdropNode.setLeftTopRightBottom(left, top + mLastCaptionHeight, left + width,
+ top + height);
+
+ // The backdrop: clear everything with the background. Clipping is done elsewhere.
+ canvas = mBackdropNode.start(width, height - mLastCaptionHeight);
+ // TODO(skuhne): mOwner.getDecorView().mBackgroundFallback.draw(..) - or similar.
+ // Note: This might not work (calculator for example uses a transparent background).
+ canvas.drawColor(0xff808080);
+ mBackdropNode.end(canvas);
+
+ // We need to render both rendered nodes explicitly.
+ mRenderer.drawRenderNode(mFrameNode);
+ mRenderer.drawRenderNode(mBackdropNode);
+ }
+
+ /**
+ * Sends a message to the renderer to wake up and perform the next action which can be
+ * either the next rendering or the self destruction if mRenderer is null.
+ * Note: This call must be synchronized.
+ */
+ private void pingRenderLocked() {
+ if (mChoreographer != null) {
+ mChoreographer.postFrameCallback(this);
+ }
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 76796243f3f5..c4347f832bd5 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -69,6 +69,12 @@ public class ResolverDrawerLayout extends ViewGroup {
private int mCollapsibleHeight;
private int mUncollapsibleHeight;
+ /**
+ * The height in pixels of reserved space added to the top of the collapsed UI;
+ * e.g. chooser targets
+ */
+ private int mCollapsibleHeightReserved;
+
private int mTopOffset;
private boolean mIsDragging;
@@ -153,12 +159,62 @@ public class ResolverDrawerLayout extends ViewGroup {
}
}
+ public void setCollapsibleHeightReserved(int heightPixels) {
+ final int oldReserved = mCollapsibleHeightReserved;
+ mCollapsibleHeightReserved = heightPixels;
+
+ final int dReserved = mCollapsibleHeightReserved - oldReserved;
+ if (dReserved != 0 && mIsDragging) {
+ mLastTouchY -= dReserved;
+ }
+
+ final int oldCollapsibleHeight = mCollapsibleHeight;
+ mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight());
+
+ if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) {
+ return;
+ }
+
+ invalidate();
+ }
+
private boolean isMoving() {
return mIsDragging || !mScroller.isFinished();
}
+ private boolean isDragging() {
+ return mIsDragging || getNestedScrollAxes() == SCROLL_AXIS_VERTICAL;
+ }
+
+ private boolean updateCollapseOffset(int oldCollapsibleHeight, boolean remainClosed) {
+ if (oldCollapsibleHeight == mCollapsibleHeight) {
+ return false;
+ }
+
+ if (isLaidOut()) {
+ final boolean isCollapsedOld = mCollapseOffset != 0;
+ if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight
+ && mCollapseOffset == oldCollapsibleHeight)) {
+ // Stay closed even at the new height.
+ mCollapseOffset = mCollapsibleHeight;
+ } else {
+ mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight);
+ }
+ final boolean isCollapsedNew = mCollapseOffset != 0;
+ if (isCollapsedOld != isCollapsedNew) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ }
+ } else {
+ // Start out collapsed at first unless we restored state for otherwise
+ mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight;
+ }
+ return true;
+ }
+
private int getMaxCollapsedHeight() {
- return isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight;
+ return (isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight)
+ + mCollapsibleHeightReserved;
}
public void setOnDismissedListener(OnDismissedListener listener) {
@@ -676,7 +732,7 @@ public class ResolverDrawerLayout extends ViewGroup {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (lp.alwaysShow && child.getVisibility() != GONE) {
measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
- heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin;
+ heightUsed += getHeightUsed(child);
}
}
@@ -688,7 +744,7 @@ public class ResolverDrawerLayout extends ViewGroup {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.alwaysShow && child.getVisibility() != GONE) {
measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed);
- heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin;
+ heightUsed += getHeightUsed(child);
}
}
@@ -697,30 +753,43 @@ public class ResolverDrawerLayout extends ViewGroup {
heightUsed - alwaysShowHeight - getMaxCollapsedHeight());
mUncollapsibleHeight = heightUsed - mCollapsibleHeight;
- if (isLaidOut()) {
- final boolean isCollapsedOld = mCollapseOffset != 0;
- if (oldCollapsibleHeight < mCollapsibleHeight
- && mCollapseOffset == oldCollapsibleHeight) {
- // Stay closed even at the new height.
- mCollapseOffset = mCollapsibleHeight;
- } else {
- mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight);
- }
- final boolean isCollapsedNew = mCollapseOffset != 0;
- if (isCollapsedOld != isCollapsedNew) {
- notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
- }
- } else {
- // Start out collapsed at first unless we restored state for otherwise
- mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight;
- }
+ updateCollapseOffset(oldCollapsibleHeight, !isDragging());
mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset;
setMeasuredDimension(sourceWidth, heightSize);
}
+ private int getHeightUsed(View child) {
+ // This method exists because we're taking a fast path at measuring ListViews that
+ // lets us get away with not doing the more expensive wrap_content measurement which
+ // imposes double child view measurement costs. If we're looking at a ListView, we can
+ // check against the lowest child view plus padding and margin instead of the actual
+ // measured height of the ListView. This lets the ListView hang off the edge when
+ // all of the content would fit on-screen.
+
+ int heightUsed = child.getMeasuredHeight();
+ if (child instanceof AbsListView) {
+ final AbsListView lv = (AbsListView) child;
+ final int lvPaddingBottom = lv.getPaddingBottom();
+
+ int lowest = 0;
+ for (int i = 0, N = lv.getChildCount(); i < N; i++) {
+ final int bottom = lv.getChildAt(i).getBottom() + lvPaddingBottom;
+ if (bottom > lowest) {
+ lowest = bottom;
+ }
+ }
+
+ if (lowest < heightUsed) {
+ heightUsed = lowest;
+ }
+ }
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ return lp.topMargin + heightUsed + lp.bottomMargin;
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int width = getWidth();
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 223dae0404c5..314e4b6f984e 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -66,10 +66,6 @@ static JMetricsID gFontMetrics_fieldID;
static jclass gFontMetricsInt_class;
static JMetricsID gFontMetricsInt_fieldID;
-static jclass gPaint_class;
-static jfieldID gPaint_nativeInstanceID;
-static jfieldID gPaint_nativeTypefaceID;
-
static void defaultSettingsForAndroid(Paint* paint) {
// GlyphID encoding is required because we are using Harfbuzz shaping
paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
@@ -82,37 +78,17 @@ struct LocaleCacheEntry {
static thread_local LocaleCacheEntry sSingleEntryLocaleCache;
-class PaintGlue {
-public:
+namespace PaintGlue {
enum MoveOpt {
AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
};
- static Paint* getNativePaint(JNIEnv* env, jobject paint) {
- SkASSERT(env);
- SkASSERT(paint);
- SkASSERT(env->IsInstanceOf(paint, gPaint_class));
- jlong paintHandle = env->GetLongField(paint, gPaint_nativeInstanceID);
- android::Paint* p = reinterpret_cast<android::Paint*>(paintHandle);
- SkASSERT(p);
- return p;
- }
-
- static TypefaceImpl* getNativeTypeface(JNIEnv* env, jobject paint) {
- SkASSERT(env);
- SkASSERT(paint);
- SkASSERT(env->IsInstanceOf(paint, gPaint_class));
- jlong typefaceHandle = env->GetLongField(paint, gPaint_nativeTypefaceID);
- android::TypefaceImpl* p = reinterpret_cast<android::TypefaceImpl*>(typefaceHandle);
- return p;
- }
-
static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
delete obj;
}
- static jlong init(JNIEnv* env, jobject clazz) {
+ static jlong init(JNIEnv* env, jobject) {
static_assert(1 << 0 == SkPaint::kAntiAlias_Flag, "paint_flags_mismatch");
static_assert(1 << 2 == SkPaint::kDither_Flag, "paint_flags_mismatch");
static_assert(1 << 3 == SkPaint::kUnderlineText_Flag, "paint_flags_mismatch");
@@ -149,9 +125,8 @@ public:
// Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
static const uint32_t sFilterBitmapFlag = 0x02;
- static jint getFlags(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- Paint* nativePaint = getNativePaint(env, paint);
+ static jint getFlags(JNIEnv* env, jobject, jlong paintHandle) {
+ Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
uint32_t result = nativePaint->getFlags();
result &= ~sFilterBitmapFlag; // Filtering no longer stored in this bit. Mask away.
if (nativePaint->getFilterQuality() != kNone_SkFilterQuality) {
@@ -160,9 +135,8 @@ public:
return static_cast<jint>(result);
}
- static void setFlags(JNIEnv* env, jobject paint, jint flags) {
- NPE_CHECK_RETURN_VOID(env, paint);
- Paint* nativePaint = getNativePaint(env, paint);
+ static void setFlags(JNIEnv* env, jobject, jlong paintHandle, jint flags) {
+ Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
// Instead of modifying 0x02, change the filter level.
nativePaint->setFilterQuality(flags & sFilterBitmapFlag
? kLow_SkFilterQuality
@@ -175,57 +149,47 @@ public:
nativePaint->setFlags(flags);
}
- static jint getHinting(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- return getNativePaint(env, paint)->getHinting()
+ static jint getHinting(JNIEnv* env, jobject, jlong paintHandle) {
+ return reinterpret_cast<Paint*>(paintHandle)->getHinting()
== Paint::kNo_Hinting ? 0 : 1;
}
- static void setHinting(JNIEnv* env, jobject paint, jint mode) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setHinting(
+ static void setHinting(JNIEnv* env, jobject, jlong paintHandle, jint mode) {
+ reinterpret_cast<Paint*>(paintHandle)->setHinting(
mode == 0 ? Paint::kNo_Hinting : Paint::kNormal_Hinting);
}
- static void setAntiAlias(JNIEnv* env, jobject paint, jboolean aa) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setAntiAlias(aa);
+ static void setAntiAlias(JNIEnv* env, jobject, jlong paintHandle, jboolean aa) {
+ reinterpret_cast<Paint*>(paintHandle)->setAntiAlias(aa);
}
- static void setLinearText(JNIEnv* env, jobject paint, jboolean linearText) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setLinearText(linearText);
+ static void setLinearText(JNIEnv* env, jobject, jlong paintHandle, jboolean linearText) {
+ reinterpret_cast<Paint*>(paintHandle)->setLinearText(linearText);
}
- static void setSubpixelText(JNIEnv* env, jobject paint, jboolean subpixelText) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setSubpixelText(subpixelText);
+ static void setSubpixelText(JNIEnv* env, jobject, jlong paintHandle, jboolean subpixelText) {
+ reinterpret_cast<Paint*>(paintHandle)->setSubpixelText(subpixelText);
}
- static void setUnderlineText(JNIEnv* env, jobject paint, jboolean underlineText) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setUnderlineText(underlineText);
+ static void setUnderlineText(JNIEnv* env, jobject, jlong paintHandle, jboolean underlineText) {
+ reinterpret_cast<Paint*>(paintHandle)->setUnderlineText(underlineText);
}
- static void setStrikeThruText(JNIEnv* env, jobject paint, jboolean strikeThruText) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setStrikeThruText(strikeThruText);
+ static void setStrikeThruText(JNIEnv* env, jobject, jlong paintHandle, jboolean strikeThruText) {
+ reinterpret_cast<Paint*>(paintHandle)->setStrikeThruText(strikeThruText);
}
- static void setFakeBoldText(JNIEnv* env, jobject paint, jboolean fakeBoldText) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setFakeBoldText(fakeBoldText);
+ static void setFakeBoldText(JNIEnv* env, jobject, jlong paintHandle, jboolean fakeBoldText) {
+ reinterpret_cast<Paint*>(paintHandle)->setFakeBoldText(fakeBoldText);
}
- static void setFilterBitmap(JNIEnv* env, jobject paint, jboolean filterBitmap) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setFilterQuality(
+ static void setFilterBitmap(JNIEnv* env, jobject, jlong paintHandle, jboolean filterBitmap) {
+ reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
}
- static void setDither(JNIEnv* env, jobject paint, jboolean dither) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setDither(dither);
+ static void setDither(JNIEnv* env, jobject, jlong paintHandle, jboolean dither) {
+ reinterpret_cast<Paint*>(paintHandle)->setDither(dither);
}
static jint getStyle(JNIEnv* env, jobject clazz,jlong objHandle) {
@@ -239,48 +203,40 @@ public:
obj->setStyle(style);
}
- static jint getColor(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
+ static jint getColor(JNIEnv* env, jobject, jlong paintHandle) {
int color;
- color = getNativePaint(env, paint)->getColor();
+ color = reinterpret_cast<Paint*>(paintHandle)->getColor();
return static_cast<jint>(color);
}
- static jint getAlpha(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
+ static jint getAlpha(JNIEnv* env, jobject, jlong paintHandle) {
int alpha;
- alpha = getNativePaint(env, paint)->getAlpha();
+ alpha = reinterpret_cast<Paint*>(paintHandle)->getAlpha();
return static_cast<jint>(alpha);
}
- static void setColor(JNIEnv* env, jobject paint, jint color) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setColor(color);
+ static void setColor(JNIEnv* env, jobject, jlong paintHandle, jint color) {
+ reinterpret_cast<Paint*>(paintHandle)->setColor(color);
}
- static void setAlpha(JNIEnv* env, jobject paint, jint a) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setAlpha(a);
+ static void setAlpha(JNIEnv* env, jobject, jlong paintHandle, jint a) {
+ reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
}
- static jfloat getStrokeWidth(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- return SkScalarToFloat(getNativePaint(env, paint)->getStrokeWidth());
+ static jfloat getStrokeWidth(JNIEnv* env, jobject, jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeWidth());
}
- static void setStrokeWidth(JNIEnv* env, jobject paint, jfloat width) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setStrokeWidth(width);
+ static void setStrokeWidth(JNIEnv* env, jobject, jlong paintHandle, jfloat width) {
+ reinterpret_cast<Paint*>(paintHandle)->setStrokeWidth(width);
}
- static jfloat getStrokeMiter(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- return SkScalarToFloat(getNativePaint(env, paint)->getStrokeMiter());
+ static jfloat getStrokeMiter(JNIEnv* env, jobject, jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeMiter());
}
- static void setStrokeMiter(JNIEnv* env, jobject paint, jfloat miter) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setStrokeMiter(miter);
+ static void setStrokeMiter(JNIEnv* env, jobject, jlong paintHandle, jfloat miter) {
+ reinterpret_cast<Paint*>(paintHandle)->setStrokeMiter(miter);
}
static jint getStrokeCap(JNIEnv* env, jobject clazz, jlong objHandle) {
@@ -417,46 +373,38 @@ public:
obj->setTextLocale(sSingleEntryLocaleCache.languageTag);
}
- static jboolean isElegantTextHeight(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- Paint* obj = getNativePaint(env, paint);
+ static jboolean isElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle) {
+ Paint* obj = reinterpret_cast<Paint*>(paintHandle);
return obj->getFontVariant() == VARIANT_ELEGANT;
}
- static void setElegantTextHeight(JNIEnv* env, jobject paint, jboolean aa) {
- NPE_CHECK_RETURN_VOID(env, paint);
- Paint* obj = getNativePaint(env, paint);
+ static void setElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle, jboolean aa) {
+ Paint* obj = reinterpret_cast<Paint*>(paintHandle);
obj->setFontVariant(aa ? VARIANT_ELEGANT : VARIANT_DEFAULT);
}
- static jfloat getTextSize(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- return SkScalarToFloat(getNativePaint(env, paint)->getTextSize());
+ static jfloat getTextSize(JNIEnv* env, jobject, jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSize());
}
- static void setTextSize(JNIEnv* env, jobject paint, jfloat textSize) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setTextSize(textSize);
+ static void setTextSize(JNIEnv* env, jobject, jlong paintHandle, jfloat textSize) {
+ reinterpret_cast<Paint*>(paintHandle)->setTextSize(textSize);
}
- static jfloat getTextScaleX(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- return SkScalarToFloat(getNativePaint(env, paint)->getTextScaleX());
+ static jfloat getTextScaleX(JNIEnv* env, jobject, jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextScaleX());
}
- static void setTextScaleX(JNIEnv* env, jobject paint, jfloat scaleX) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setTextScaleX(scaleX);
+ static void setTextScaleX(JNIEnv* env, jobject, jlong paintHandle, jfloat scaleX) {
+ reinterpret_cast<Paint*>(paintHandle)->setTextScaleX(scaleX);
}
- static jfloat getTextSkewX(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
- return SkScalarToFloat(getNativePaint(env, paint)->getTextSkewX());
+ static jfloat getTextSkewX(JNIEnv* env, jobject, jlong paintHandle) {
+ return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSkewX());
}
- static void setTextSkewX(JNIEnv* env, jobject paint, jfloat skewX) {
- NPE_CHECK_RETURN_VOID(env, paint);
- getNativePaint(env, paint)->setTextSkewX(skewX);
+ static void setTextSkewX(JNIEnv* env, jobject, jlong paintHandle, jfloat skewX) {
+ reinterpret_cast<Paint*>(paintHandle)->setTextSkewX(skewX);
}
static jfloat getLetterSpacing(JNIEnv* env, jobject clazz, jlong paintHandle) {
@@ -489,14 +437,15 @@ public:
paint->setHyphenEdit((uint32_t)hyphen);
}
- static SkScalar getMetricsInternal(JNIEnv* env, jobject jpaint, Paint::FontMetrics *metrics) {
+ static SkScalar getMetricsInternal(jlong paintHandle, jlong typefaceHandle,
+ Paint::FontMetrics *metrics) {
const int kElegantTop = 2500;
const int kElegantBottom = -1000;
const int kElegantAscent = 1900;
const int kElegantDescent = -500;
const int kElegantLeading = 0;
- Paint* paint = getNativePaint(env, jpaint);
- TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
+ Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+ TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
typeface = TypefaceImpl_resolveDefault(typeface);
FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
float saveSkewX = paint->getTextSkewX();
@@ -520,24 +469,22 @@ public:
return spacing;
}
- static jfloat ascent(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
+ static jfloat ascent(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle) {
Paint::FontMetrics metrics;
- getMetricsInternal(env, paint, &metrics);
+ getMetricsInternal(paintHandle, typefaceHandle, &metrics);
return SkScalarToFloat(metrics.fAscent);
}
- static jfloat descent(JNIEnv* env, jobject paint) {
- NPE_CHECK_RETURN_ZERO(env, paint);
+ static jfloat descent(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle) {
Paint::FontMetrics metrics;
- getMetricsInternal(env, paint, &metrics);
+ getMetricsInternal(paintHandle, typefaceHandle, &metrics);
return SkScalarToFloat(metrics.fDescent);
}
- static jfloat getFontMetrics(JNIEnv* env, jobject paint, jobject metricsObj) {
- NPE_CHECK_RETURN_ZERO(env, paint);
+ static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle,
+ jlong typefaceHandle, jobject metricsObj) {
Paint::FontMetrics metrics;
- SkScalar spacing = getMetricsInternal(env, paint, &metrics);
+ SkScalar spacing = getMetricsInternal(paintHandle, typefaceHandle, &metrics);
if (metricsObj) {
SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
@@ -550,11 +497,11 @@ public:
return SkScalarToFloat(spacing);
}
- static jint getFontMetricsInt(JNIEnv* env, jobject paint, jobject metricsObj) {
- NPE_CHECK_RETURN_ZERO(env, paint);
+ static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle,
+ jlong typefaceHandle, jobject metricsObj) {
Paint::FontMetrics metrics;
- getMetricsInternal(env, paint, &metrics);
+ getMetricsInternal(paintHandle, typefaceHandle, &metrics);
int ascent = SkScalarRoundToInt(metrics.fAscent);
int descent = SkScalarRoundToInt(metrics.fDescent);
int leading = SkScalarRoundToInt(metrics.fLeading);
@@ -573,7 +520,6 @@ public:
static jfloat doTextAdvances(JNIEnv *env, Paint *paint, TypefaceImpl* typeface,
const jchar *text, jint start, jint count, jint contextCount, jint bidiFlags,
jfloatArray advances, jint advancesIndex) {
- NPE_CHECK_RETURN_ZERO(env, paint);
NPE_CHECK_RETURN_ZERO(env, text);
if ((start | count | contextCount | advancesIndex) < 0 || contextCount < count) {
@@ -841,7 +787,7 @@ public:
static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle,
jstring text, jint start, jint end, jint bidiFlags, jobject bounds) {
- const Paint* paint = reinterpret_cast<Paint*>(paintHandle);;
+ const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
const jchar* textArray = env->GetStringChars(text, NULL);
doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags);
@@ -961,97 +907,97 @@ public:
return result;
}
-};
+}; // namespace PaintGlue
static const JNINativeMethod methods[] = {
- {"finalizer", "(J)V", (void*) PaintGlue::finalizer},
- {"native_init","()J", (void*) PaintGlue::init},
- {"native_initWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
-
- {"native_reset","!(J)V", (void*) PaintGlue::reset},
- {"native_set","!(JJ)V", (void*) PaintGlue::assign},
- {"getFlags","!()I", (void*) PaintGlue::getFlags},
- {"setFlags","!(I)V", (void*) PaintGlue::setFlags},
- {"getHinting","!()I", (void*) PaintGlue::getHinting},
- {"setHinting","!(I)V", (void*) PaintGlue::setHinting},
- {"setAntiAlias","!(Z)V", (void*) PaintGlue::setAntiAlias},
- {"setSubpixelText","!(Z)V", (void*) PaintGlue::setSubpixelText},
- {"setLinearText","!(Z)V", (void*) PaintGlue::setLinearText},
- {"setUnderlineText","!(Z)V", (void*) PaintGlue::setUnderlineText},
- {"setStrikeThruText","!(Z)V", (void*) PaintGlue::setStrikeThruText},
- {"setFakeBoldText","!(Z)V", (void*) PaintGlue::setFakeBoldText},
- {"setFilterBitmap","!(Z)V", (void*) PaintGlue::setFilterBitmap},
- {"setDither","!(Z)V", (void*) PaintGlue::setDither},
- {"native_getStyle","!(J)I", (void*) PaintGlue::getStyle},
- {"native_setStyle","!(JI)V", (void*) PaintGlue::setStyle},
- {"getColor","!()I", (void*) PaintGlue::getColor},
- {"setColor","!(I)V", (void*) PaintGlue::setColor},
- {"getAlpha","!()I", (void*) PaintGlue::getAlpha},
- {"setAlpha","!(I)V", (void*) PaintGlue::setAlpha},
- {"getStrokeWidth","!()F", (void*) PaintGlue::getStrokeWidth},
- {"setStrokeWidth","!(F)V", (void*) PaintGlue::setStrokeWidth},
- {"getStrokeMiter","!()F", (void*) PaintGlue::getStrokeMiter},
- {"setStrokeMiter","!(F)V", (void*) PaintGlue::setStrokeMiter},
- {"native_getStrokeCap","!(J)I", (void*) PaintGlue::getStrokeCap},
- {"native_setStrokeCap","!(JI)V", (void*) PaintGlue::setStrokeCap},
- {"native_getStrokeJoin","!(J)I", (void*) PaintGlue::getStrokeJoin},
- {"native_setStrokeJoin","!(JI)V", (void*) PaintGlue::setStrokeJoin},
- {"native_getFillPath","!(JJJ)Z", (void*) PaintGlue::getFillPath},
- {"native_setShader","!(JJ)J", (void*) PaintGlue::setShader},
- {"native_setColorFilter","!(JJ)J", (void*) PaintGlue::setColorFilter},
- {"native_setXfermode","!(JJ)J", (void*) PaintGlue::setXfermode},
- {"native_setPathEffect","!(JJ)J", (void*) PaintGlue::setPathEffect},
- {"native_setMaskFilter","!(JJ)J", (void*) PaintGlue::setMaskFilter},
- {"native_setTypeface","!(JJ)J", (void*) PaintGlue::setTypeface},
- {"native_setRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
- {"native_getTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
- {"native_setTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
- {"native_setTextLocale","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale},
- {"isElegantTextHeight","!()Z", (void*) PaintGlue::isElegantTextHeight},
- {"setElegantTextHeight","!(Z)V", (void*) PaintGlue::setElegantTextHeight},
- {"getTextSize","!()F", (void*) PaintGlue::getTextSize},
- {"setTextSize","!(F)V", (void*) PaintGlue::setTextSize},
- {"getTextScaleX","!()F", (void*) PaintGlue::getTextScaleX},
- {"setTextScaleX","!(F)V", (void*) PaintGlue::setTextScaleX},
- {"getTextSkewX","!()F", (void*) PaintGlue::getTextSkewX},
- {"setTextSkewX","!(F)V", (void*) PaintGlue::setTextSkewX},
- {"native_getLetterSpacing","!(J)F", (void*) PaintGlue::getLetterSpacing},
- {"native_setLetterSpacing","!(JF)V", (void*) PaintGlue::setLetterSpacing},
- {"native_setFontFeatureSettings","(JLjava/lang/String;)V",
+ {"nFinalizer", "(J)V", (void*) PaintGlue::finalizer},
+ {"nInit","()J", (void*) PaintGlue::init},
+ {"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
+
+ {"nReset","!(J)V", (void*) PaintGlue::reset},
+ {"nSet","!(JJ)V", (void*) PaintGlue::assign},
+ {"nGetFlags","!(J)I", (void*) PaintGlue::getFlags},
+ {"nSetFlags","!(JI)V", (void*) PaintGlue::setFlags},
+ {"nGetHinting","!(J)I", (void*) PaintGlue::getHinting},
+ {"nSetHinting","!(JI)V", (void*) PaintGlue::setHinting},
+ {"nSetAntiAlias","!(JZ)V", (void*) PaintGlue::setAntiAlias},
+ {"nSetSubpixelText","!(JZ)V", (void*) PaintGlue::setSubpixelText},
+ {"nSetLinearText","!(JZ)V", (void*) PaintGlue::setLinearText},
+ {"nSetUnderlineText","!(JZ)V", (void*) PaintGlue::setUnderlineText},
+ {"nSetStrikeThruText","!(JZ)V", (void*) PaintGlue::setStrikeThruText},
+ {"nSetFakeBoldText","!(JZ)V", (void*) PaintGlue::setFakeBoldText},
+ {"nSetFilterBitmap","!(JZ)V", (void*) PaintGlue::setFilterBitmap},
+ {"nSetDither","!(JZ)V", (void*) PaintGlue::setDither},
+ {"nGetStyle","!(J)I", (void*) PaintGlue::getStyle},
+ {"nSetStyle","!(JI)V", (void*) PaintGlue::setStyle},
+ {"nGetColor","!(J)I", (void*) PaintGlue::getColor},
+ {"nSetColor","!(JI)V", (void*) PaintGlue::setColor},
+ {"nGetAlpha","!(J)I", (void*) PaintGlue::getAlpha},
+ {"nSetAlpha","!(JI)V", (void*) PaintGlue::setAlpha},
+ {"nGetStrokeWidth","!(J)F", (void*) PaintGlue::getStrokeWidth},
+ {"nSetStrokeWidth","!(JF)V", (void*) PaintGlue::setStrokeWidth},
+ {"nGetStrokeMiter","!(J)F", (void*) PaintGlue::getStrokeMiter},
+ {"nSetStrokeMiter","!(JF)V", (void*) PaintGlue::setStrokeMiter},
+ {"nGetStrokeCap","!(J)I", (void*) PaintGlue::getStrokeCap},
+ {"nSetStrokeCap","!(JI)V", (void*) PaintGlue::setStrokeCap},
+ {"nGetStrokeJoin","!(J)I", (void*) PaintGlue::getStrokeJoin},
+ {"nSetStrokeJoin","!(JI)V", (void*) PaintGlue::setStrokeJoin},
+ {"nGetFillPath","!(JJJ)Z", (void*) PaintGlue::getFillPath},
+ {"nSetShader","!(JJ)J", (void*) PaintGlue::setShader},
+ {"nSetColorFilter","!(JJ)J", (void*) PaintGlue::setColorFilter},
+ {"nSetXfermode","!(JJ)J", (void*) PaintGlue::setXfermode},
+ {"nSetPathEffect","!(JJ)J", (void*) PaintGlue::setPathEffect},
+ {"nSetMaskFilter","!(JJ)J", (void*) PaintGlue::setMaskFilter},
+ {"nSetTypeface","!(JJ)J", (void*) PaintGlue::setTypeface},
+ {"nSetRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
+ {"nGetTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
+ {"nSetTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
+ {"nSetTextLocale","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale},
+ {"nIsElegantTextHeight","!(J)Z", (void*) PaintGlue::isElegantTextHeight},
+ {"nSetElegantTextHeight","!(JZ)V", (void*) PaintGlue::setElegantTextHeight},
+ {"nGetTextSize","!(J)F", (void*) PaintGlue::getTextSize},
+ {"nSetTextSize","!(JF)V", (void*) PaintGlue::setTextSize},
+ {"nGetTextScaleX","!(J)F", (void*) PaintGlue::getTextScaleX},
+ {"nSetTextScaleX","!(JF)V", (void*) PaintGlue::setTextScaleX},
+ {"nGetTextSkewX","!(J)F", (void*) PaintGlue::getTextSkewX},
+ {"nSetTextSkewX","!(JF)V", (void*) PaintGlue::setTextSkewX},
+ {"nGetLetterSpacing","!(J)F", (void*) PaintGlue::getLetterSpacing},
+ {"nSetLetterSpacing","!(JF)V", (void*) PaintGlue::setLetterSpacing},
+ {"nSetFontFeatureSettings","(JLjava/lang/String;)V",
(void*) PaintGlue::setFontFeatureSettings},
- {"native_getHyphenEdit", "!(J)I", (void*) PaintGlue::getHyphenEdit},
- {"native_setHyphenEdit", "!(JI)V", (void*) PaintGlue::setHyphenEdit},
- {"ascent","!()F", (void*) PaintGlue::ascent},
- {"descent","!()F", (void*) PaintGlue::descent},
+ {"nGetHyphenEdit", "!(J)I", (void*) PaintGlue::getHyphenEdit},
+ {"nSetHyphenEdit", "!(JI)V", (void*) PaintGlue::setHyphenEdit},
+ {"nAscent","!(JJ)F", (void*) PaintGlue::ascent},
+ {"nDescent","!(JJ)F", (void*) PaintGlue::descent},
- {"getFontMetrics", "!(Landroid/graphics/Paint$FontMetrics;)F",
+ {"nGetFontMetrics", "!(JJLandroid/graphics/Paint$FontMetrics;)F",
(void*)PaintGlue::getFontMetrics},
- {"getFontMetricsInt", "!(Landroid/graphics/Paint$FontMetricsInt;)I",
+ {"nGetFontMetricsInt", "!(JJLandroid/graphics/Paint$FontMetricsInt;)I",
(void*)PaintGlue::getFontMetricsInt},
- {"native_breakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC},
- {"native_breakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
- {"native_getTextAdvances","(JJ[CIIIII[FI)F",
+ {"nBreakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC},
+ {"nBreakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
+ {"nGetTextAdvances","(JJ[CIIIII[FI)F",
(void*) PaintGlue::getTextAdvances___CIIIII_FI},
- {"native_getTextAdvances","(JJLjava/lang/String;IIIII[FI)F",
+ {"nGetTextAdvances","(JJLjava/lang/String;IIIII[FI)F",
(void*) PaintGlue::getTextAdvances__StringIIIII_FI},
- {"native_getTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
- {"native_getTextRunCursor", "(JLjava/lang/String;IIIII)I",
+ {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
+ {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I",
(void*) PaintGlue::getTextRunCursor__String},
- {"native_getTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
- {"native_getTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
- {"nativeGetStringBounds", "(JJLjava/lang/String;IIILandroid/graphics/Rect;)V",
+ {"nGetTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
+ {"nGetTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
+ {"nGetStringBounds", "(JJLjava/lang/String;IIILandroid/graphics/Rect;)V",
(void*) PaintGlue::getStringBounds },
- {"nativeGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V",
+ {"nGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V",
(void*) PaintGlue::getCharArrayBounds },
- {"native_hasGlyph", "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
- {"native_getRunAdvance", "(JJ[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
- {"native_getOffsetForAdvance", "(JJ[CIIIIZF)I",
+ {"nHasGlyph", "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
+ {"nGetRunAdvance", "(JJ[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
+ {"nGetOffsetForAdvance", "(JJ[CIIIIZF)I",
(void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
- {"native_setShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer},
- {"native_hasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer}
+ {"nSetShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer},
+ {"nHasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer}
};
int register_android_graphics_Paint(JNIEnv* env) {
@@ -1073,10 +1019,6 @@ int register_android_graphics_Paint(JNIEnv* env) {
gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
- gPaint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Paint"));
- gPaint_nativeInstanceID = GetFieldIDOrDie(env, gPaint_class, "mNativePaint", "J");
- gPaint_nativeTypefaceID = GetFieldIDOrDie(env, gPaint_class, "mNativeTypeface", "J");
-
return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
}
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 32a877ae3398..3d96fab9709d 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -749,57 +749,57 @@ static void freeTextLayoutCaches(JNIEnv* env, jobject) {
static const JNINativeMethod gMethods[] = {
{"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
{"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
- {"native_setBitmap", "(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
- {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
- {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth},
- {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight},
- {"native_setHighContrastText","(JZ)V", (void*) CanvasJNI::setHighContrastText},
- {"native_save","(JI)I", (void*) CanvasJNI::save},
- {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
- {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
- {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
- {"native_restore","(JZ)V", (void*) CanvasJNI::restore},
- {"native_restoreToCount","(JIZ)V", (void*) CanvasJNI::restoreToCount},
- {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM},
- {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
- {"native_concat","(JJ)V", (void*) CanvasJNI::concat},
- {"native_rotate","(JF)V", (void*) CanvasJNI::rotate},
- {"native_scale","(JFF)V", (void*) CanvasJNI::scale},
- {"native_skew","(JFF)V", (void*) CanvasJNI::skew},
- {"native_translate","(JFF)V", (void*) CanvasJNI::translate},
- {"native_getClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
- {"native_quickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
- {"native_quickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
- {"native_clipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
- {"native_clipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
- {"native_clipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion},
- {"native_drawColor","(JII)V", (void*) CanvasJNI::drawColor},
- {"native_drawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
- {"native_drawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
- {"native_drawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
- {"native_drawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
- {"native_drawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
- {"native_drawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
- {"native_drawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion },
- {"native_drawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
- {"native_drawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
- {"native_drawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
- {"native_drawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
- {"native_drawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
- {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
- {"native_drawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch},
- {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap},
- {"nativeDrawBitmapMatrix", "(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix},
- {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
- {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
- {"nativeDrawBitmapMesh", "(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
- {"native_drawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
- {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
- {"native_drawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
- {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
- {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
- {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
- {"nativeSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter},
+ {"native_setBitmap", "!(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
+ {"native_isOpaque","!(J)Z", (void*) CanvasJNI::isOpaque},
+ {"native_getWidth","!(J)I", (void*) CanvasJNI::getWidth},
+ {"native_getHeight","!(J)I", (void*) CanvasJNI::getHeight},
+ {"native_setHighContrastText","!(JZ)V", (void*) CanvasJNI::setHighContrastText},
+ {"native_save","!(JI)I", (void*) CanvasJNI::save},
+ {"native_saveLayer","!(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
+ {"native_saveLayerAlpha","!(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
+ {"native_getSaveCount","!(J)I", (void*) CanvasJNI::getSaveCount},
+ {"native_restore","!(JZ)V", (void*) CanvasJNI::restore},
+ {"native_restoreToCount","!(JIZ)V", (void*) CanvasJNI::restoreToCount},
+ {"native_getCTM", "!(JJ)V", (void*)CanvasJNI::getCTM},
+ {"native_setMatrix","!(JJ)V", (void*) CanvasJNI::setMatrix},
+ {"native_concat","!(JJ)V", (void*) CanvasJNI::concat},
+ {"native_rotate","!(JF)V", (void*) CanvasJNI::rotate},
+ {"native_scale","!(JFF)V", (void*) CanvasJNI::scale},
+ {"native_skew","!(JFF)V", (void*) CanvasJNI::skew},
+ {"native_translate","!(JFF)V", (void*) CanvasJNI::translate},
+ {"native_getClipBounds","!(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
+ {"native_quickReject","!(JJ)Z", (void*) CanvasJNI::quickRejectPath},
+ {"native_quickReject","!(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
+ {"native_clipRect","!(JFFFFI)Z", (void*) CanvasJNI::clipRect},
+ {"native_clipPath","!(JJI)Z", (void*) CanvasJNI::clipPath},
+ {"native_clipRegion","!(JJI)Z", (void*) CanvasJNI::clipRegion},
+ {"native_drawColor","!(JII)V", (void*) CanvasJNI::drawColor},
+ {"native_drawPaint","!(JJ)V", (void*) CanvasJNI::drawPaint},
+ {"native_drawPoint", "!(JFFJ)V", (void*) CanvasJNI::drawPoint},
+ {"native_drawPoints", "!(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
+ {"native_drawLine", "!(JFFFFJ)V", (void*) CanvasJNI::drawLine},
+ {"native_drawLines", "!(J[FIIJ)V", (void*) CanvasJNI::drawLines},
+ {"native_drawRect","!(JFFFFJ)V", (void*) CanvasJNI::drawRect},
+ {"native_drawRegion", "!(JJJ)V", (void*) CanvasJNI::drawRegion },
+ {"native_drawRoundRect","!(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
+ {"native_drawCircle","!(JFFFJ)V", (void*) CanvasJNI::drawCircle},
+ {"native_drawOval","!(JFFFFJ)V", (void*) CanvasJNI::drawOval},
+ {"native_drawArc","!(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
+ {"native_drawPath","!(JJJ)V", (void*) CanvasJNI::drawPath},
+ {"nativeDrawVertices", "!(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
+ {"native_drawNinePatch", "!(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch},
+ {"native_drawBitmap","!(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap},
+ {"nativeDrawBitmapMatrix", "!(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix},
+ {"native_drawBitmap","!(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
+ {"native_drawBitmap", "!(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
+ {"nativeDrawBitmapMesh", "!(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
+ {"native_drawText","!(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
+ {"native_drawText","!(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
+ {"native_drawTextRun","!(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
+ {"native_drawTextRun","!(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
+ {"native_drawTextOnPath","!(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
+ {"native_drawTextOnPath","!(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
+ {"nativeSetDrawFilter", "!(JJ)V", (void*) CanvasJNI::setDrawFilter},
{"freeCaches", "()V", (void*) CanvasJNI::freeCaches},
{"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}
};
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 437bd192b70a..24eb961bd431 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -267,7 +267,7 @@ static const JNINativeMethod gMethods[] = {
{ "nativeDispose",
"(J)V",
(void*)nativeDispose },
- { "nativeScheduleVsync", "(J)V",
+ { "nativeScheduleVsync", "!(J)V",
(void*)nativeScheduleVsync }
};
diff --git a/core/jni/android_view_DisplayListCanvas.cpp b/core/jni/android_view_DisplayListCanvas.cpp
index 460c1a1a2ab9..b64acc32e634 100644
--- a/core/jni/android_view_DisplayListCanvas.cpp
+++ b/core/jni/android_view_DisplayListCanvas.cpp
@@ -174,24 +174,24 @@ android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject java
const char* const kClassPathName = "android/view/DisplayListCanvas";
static JNINativeMethod gMethods[] = {
- { "nIsAvailable", "()Z", (void*) android_view_DisplayListCanvas_isAvailable },
- { "nInsertReorderBarrier","(JZ)V", (void*) android_view_DisplayListCanvas_insertReorderBarrier },
+ { "nIsAvailable", "!()Z", (void*) android_view_DisplayListCanvas_isAvailable },
+ { "nInsertReorderBarrier","!(JZ)V", (void*) android_view_DisplayListCanvas_insertReorderBarrier },
- { "nCallDrawGLFunction", "(JJ)V", (void*) android_view_DisplayListCanvas_callDrawGLFunction },
+ { "nCallDrawGLFunction", "!(JJ)V", (void*) android_view_DisplayListCanvas_callDrawGLFunction },
- { "nDrawRoundRect", "(JJJJJJJJ)V", (void*) android_view_DisplayListCanvas_drawRoundRectProps },
- { "nDrawCircle", "(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps },
+ { "nDrawRoundRect", "!(JJJJJJJJ)V", (void*) android_view_DisplayListCanvas_drawRoundRectProps },
+ { "nDrawCircle", "!(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps },
- { "nFinishRecording", "(J)J", (void*) android_view_DisplayListCanvas_finishRecording },
- { "nDrawRenderNode", "(JJ)V", (void*) android_view_DisplayListCanvas_drawRenderNode },
+ { "nFinishRecording", "!(J)J", (void*) android_view_DisplayListCanvas_finishRecording },
+ { "nDrawRenderNode", "!(JJ)V", (void*) android_view_DisplayListCanvas_drawRenderNode },
- { "nCreateDisplayListCanvas", "(II)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
- { "nResetDisplayListCanvas", "(JII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
+ { "nCreateDisplayListCanvas", "!(II)J", (void*) android_view_DisplayListCanvas_createDisplayListCanvas },
+ { "nResetDisplayListCanvas", "!(JII)V", (void*) android_view_DisplayListCanvas_resetDisplayListCanvas },
- { "nDrawLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawLayer },
+ { "nDrawLayer", "!(JJ)V", (void*) android_view_DisplayListCanvas_drawLayer },
- { "nGetMaximumTextureWidth", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureWidth },
- { "nGetMaximumTextureHeight", "()I", (void*) android_view_DisplayListCanvas_getMaxTextureHeight },
+ { "nGetMaximumTextureWidth", "!()I", (void*) android_view_DisplayListCanvas_getMaxTextureWidth },
+ { "nGetMaximumTextureHeight", "!()I", (void*) android_view_DisplayListCanvas_getMaxTextureHeight },
};
static JNINativeMethod gActivityThreadMethods[] = {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index c79f833b54a9..17eb876aebf3 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -461,10 +461,10 @@ static void android_view_ThreadedRendererd_drawRenderNode(JNIEnv* env, jobject c
proxy->drawRenderNode(renderNode);
}
-static void android_view_ThreadedRenderer_setContentOverdrawProtectionBounds(JNIEnv* env,
+static void android_view_ThreadedRenderer_setContentDrawBounds(JNIEnv* env,
jobject clazz, jlong proxyPtr, jint left, jint top, jint right, jint bottom) {
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
- proxy->setContentOverdrawProtectionBounds(left, top, right, bottom);
+ proxy->setContentDrawBounds(left, top, right, bottom);
}
// ----------------------------------------------------------------------------
@@ -522,8 +522,7 @@ static const JNINativeMethod gMethods[] = {
{ "nAddRenderNode", "(JJZ)V", (void*) android_view_ThreadedRenderer_addRenderNode},
{ "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
{ "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode},
- { "nSetContentOverdrawProtectionBounds", "(JIIII)V",
- (void*)android_view_ThreadedRenderer_setContentOverdrawProtectionBounds},
+ { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds},
};
int register_android_view_ThreadedRenderer(JNIEnv* env) {
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index dcdfb6c318d4..41726fb71f7f 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -85,7 +85,7 @@
<ListView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:id="@+id/resolver_list"
android:clipToPadding="false"
android:scrollbarStyle="outsideOverlay"
diff --git a/core/res/res/layout/list_content.xml b/core/res/res/layout/list_content.xml
index 14140322b7cd..45ade4d0c6d7 100644
--- a/core/res/res/layout/list_content.xml
+++ b/core/res/res/layout/list_content.xml
@@ -44,8 +44,7 @@
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:drawSelectorOnTop="false" />
+ android:layout_height="match_parent" />
<TextView android:id="@+android:id/internalEmpty"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/core/res/res/layout/preference_list_fragment.xml b/core/res/res/layout/preference_list_fragment.xml
index 4e895b0417ff..f073c3379727 100644
--- a/core/res/res/layout/preference_list_fragment.xml
+++ b/core/res/res/layout/preference_list_fragment.xml
@@ -41,6 +41,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/preference_fragment_padding_side"
+ android:textAppearance="?android:attr/textAppearanceMedium"
android:gravity="center"
android:visibility="gone" />
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 1c496f6435d6..0a7ac776744a 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -70,6 +70,7 @@
android:minLines="2"
android:maxLines="2"
android:gravity="top|center_horizontal"
- android:ellipsize="marquee" />
+ android:ellipsize="marquee"
+ android:visibility="gone" />
</LinearLayout>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 45d9dc36adae..5032c2538c94 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -51,7 +51,7 @@
<string name="serviceDisabled" msgid="1937553226592516411">"سرویس غیرفعال شده است."</string>
<string name="serviceRegistered" msgid="6275019082598102493">"ثبت با موفقیت انجام شد"</string>
<string name="serviceErased" msgid="1288584695297200972">"پاک کردن با موفقیت انجام شد."</string>
- <string name="passwordIncorrect" msgid="7612208839450128715">"رمز ورود اشتباه است."</string>
+ <string name="passwordIncorrect" msgid="7612208839450128715">"گذرواژه اشتباه است."</string>
<string name="mmiComplete" msgid="8232527495411698359">"‏MMI کامل شد."</string>
<string name="badPin" msgid="9015277645546710014">"‏پین قدیمی که نوشته‎اید صحیح نیست."</string>
<string name="badPuk" msgid="5487257647081132201">"‏PUK که نوشته‌اید صحیح نیست."</string>
@@ -74,7 +74,7 @@
<string name="CfMmi" msgid="5123218989141573515">"هدایت تماس"</string>
<string name="CwMmi" msgid="9129678056795016867">"انتظار تماس"</string>
<string name="BaMmi" msgid="455193067926770581">"محدودیت تماس"</string>
- <string name="PwdMmi" msgid="7043715687905254199">"تغییر رمز ورود"</string>
+ <string name="PwdMmi" msgid="7043715687905254199">"تغییر گذرواژه"</string>
<string name="PinMmi" msgid="3113117780361190304">"تغییر پین"</string>
<string name="CnipMmi" msgid="3110534680557857162">"شماره تماس حاضر"</string>
<string name="CnirMmi" msgid="3062102121430548731">"شماره تماس محدود شده"</string>
@@ -495,7 +495,7 @@
<string name="permdesc_bindCarrierServices" msgid="1391552602551084192">"به دارنده امکان می‌دهد به سرویس‌های شرکت مخابراتی متصل شود. هرگز نباید برای برنامه‌های عادی مورد نیاز باشد."</string>
<string name="permlab_access_notification_policy" msgid="4247510821662059671">"دسترسی به حالت «مزاحم نشوید»"</string>
<string name="permdesc_access_notification_policy" msgid="3296832375218749580">"به برنامه امکان می‌دهد پیکربندی «مزاحم نشوید» را بخواند و بنویسد."</string>
- <string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین رمز ورود"</string>
+ <string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین گذرواژه"</string>
<string name="policydesc_limitPassword" msgid="2502021457917874968">"کنترل طول و نوع نویسه‌هایی که در گذرواژه و پین قفل صفحه مجاز است."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"نمایش تلاش‌های قفل گشایی صفحه"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"‏تعداد گذرواژه‎های نادرست تایپ شده را هنگام بازکردن قفل صفحه کنترل می‌کند، و اگر دفعات زیادی گذرواژه نادرست وارد شود رایانهٔ لوحی را قفل می‌کند و همه داده‎های رایانهٔ لوحی را پاک می‌کند."</string>
@@ -532,7 +532,7 @@
<item msgid="7897544654242874543">"محل کار"</item>
<item msgid="1103601433382158155">"نمابر محل کار"</item>
<item msgid="1735177144948329370">"نمابر خانه"</item>
- <item msgid="603878674477207394">"پیجر"</item>
+ <item msgid="603878674477207394">"پی‌جو"</item>
<item msgid="1650824275177931637">"سایر موارد"</item>
<item msgid="9192514806975898961">"سفارشی"</item>
</string-array>
@@ -575,7 +575,7 @@
<string name="phoneTypeWork" msgid="8863939667059911633">"محل کار"</string>
<string name="phoneTypeFaxWork" msgid="3517792160008890912">"نمابر محل کار"</string>
<string name="phoneTypeFaxHome" msgid="2067265972322971467">"نمابر خانه"</string>
- <string name="phoneTypePager" msgid="7582359955394921732">"پیجر"</string>
+ <string name="phoneTypePager" msgid="7582359955394921732">"پی‌جو"</string>
<string name="phoneTypeOther" msgid="1544425847868765990">"سایر موارد"</string>
<string name="phoneTypeCallback" msgid="2712175203065678206">"برگرداندن تماس"</string>
<string name="phoneTypeCar" msgid="8738360689616716982">"خودرو"</string>
@@ -700,9 +700,9 @@
<string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"‏تلاش‎های زیادی برای کشیدن الگو صورت گرفته است"</string>
<string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"‏برای بازگشایی قفل، با حساب Google خود وارد سیستم شوید."</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"نام کاربری (ایمیل)"</string>
- <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"رمز ورود"</string>
+ <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"گذرواژه"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ورود به سیستم"</string>
- <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"نام کاربر یا رمز ورود نامعتبر است."</string>
+ <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"نام کاربر یا گذرواژه نامعتبر است."</string>
<string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"‏نام کاربری یا گذرواژهٔ خود را فراموش کردید؟\nاز "<b>"google.com/accounts/recovery"</b>" بازدید کنید."</string>
<string name="lockscreen_glogin_checking_password" msgid="7114627351286933867">"در حال بررسی..."</string>
<string name="lockscreen_unlock_label" msgid="737440483220667054">"بازگشایی قفل"</string>
@@ -786,7 +786,7 @@
<string name="permdesc_addVoicemail" msgid="6604508651428252437">"به برنامه اجازه می‌دهد تا پیام‌ها را به صندوق دریافت پست صوتی شما اضافه کند."</string>
<string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"تغییر مجوزهای مکان جغرافیایی مرورگر"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"‏به برنامه اجازه می‎دهد تا مجوزهای جغرافیایی مرورگر را تغییر دهد. برنامه‎های مخرب می‎توانند از آن استفاده کنند تا اطلاعات موقعیت مکانی را به سایت‌های وب کتابخانه بفرستند."</string>
- <string name="save_password_message" msgid="767344687139195790">"می‌خواهید مرورگر این رمز ورود را به خاطر داشته باشد؟"</string>
+ <string name="save_password_message" msgid="767344687139195790">"می‌خواهید مرورگر این گذرواژه را به خاطر داشته باشد؟"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"اکنون نه"</string>
<string name="save_password_remember" msgid="6491879678996749466">"به خاطر سپردن"</string>
<string name="save_password_never" msgid="8274330296785855105">"هیچ‌وقت"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 72b1c54293d6..a034a256546c 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -290,7 +290,7 @@
<string name="permdesc_enableCarMode" msgid="4853187425751419467">"Consente all\'applicazione di abilitare la modalità automobile."</string>
<string name="permlab_killBackgroundProcesses" msgid="3914026687420177202">"chiusura altre applicazioni"</string>
<string name="permdesc_killBackgroundProcesses" msgid="4593353235959733119">"Consente all\'applicazione di terminare i processi in background di altre applicazioni. Ciò potrebbe causare l\'interruzione di altre applicazioni."</string>
- <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"spostamento sopra altre app"</string>
+ <string name="permlab_systemAlertWindow" msgid="3543347980839518613">"posizionamento davanti ad altre app"</string>
<string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Consente all\'applicazione di spostarsi sopra ad altre applicazioni o parti dell\'interfaccia utente. Potrebbe interferire con il tuo utilizzo dell\'interfaccia in qualsiasi applicazione o cambiare ciò che credi di vedere in altre applicazioni."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"esecuzione permanente delle applicazioni"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Consente all\'applicazione di rendere persistenti in memoria alcune sue parti. Ciò può limitare la memoria disponibile per altre applicazioni, rallentando il tablet."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 29fd6a1be775..e803cbb2dca0 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -163,7 +163,7 @@
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"För många <xliff:g id="CONTENT_TYPE">%s</xliff:g>-borttagningar."</string>
<string name="low_memory" product="tablet" msgid="6494019234102154896">"Pekdatorns lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string>
<string name="low_memory" product="watch" msgid="4415914910770005166">"Klockans lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string>
- <string name="low_memory" product="tv" msgid="516619861191025923">"Lagringsutrymmet på tv:n är fullt. Ta bort några filer för att frigöra utrymme."</string>
+ <string name="low_memory" product="tv" msgid="516619861191025923">"Lagringsutrymmet på TV:n är fullt. Ta bort några filer för att frigöra utrymme."</string>
<string name="low_memory" product="default" msgid="3475999286680000541">"Mobilens lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme."</string>
<string name="ssl_ca_cert_warning" msgid="5848402127455021714">"Nätverket kan vara övervakat"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4475437862189850602">"Av en okänd tredje part"</string>
@@ -177,7 +177,7 @@
<string name="factory_reset_message" msgid="4905025204141900666">"Administratörsappen saknar delar eller är skadad och kan inte användas. Enheten kommer nu att rensas. Kontakta administratören om du behöver hjälp."</string>
<string name="me" msgid="6545696007631404292">"Jag"</string>
<string name="power_dialog" product="tablet" msgid="8545351420865202853">"Alternativ för surfplattan"</string>
- <string name="power_dialog" product="tv" msgid="6153888706430556356">"Tv-alternativ"</string>
+ <string name="power_dialog" product="tv" msgid="6153888706430556356">"TV-alternativ"</string>
<string name="power_dialog" product="default" msgid="1319919075463988638">"Telefonalternativ"</string>
<string name="silent_mode" msgid="7167703389802618663">"Tyst läge"</string>
<string name="turn_on_radio" msgid="3912793092339962371">"Aktivera trådlöst"</string>
@@ -195,7 +195,7 @@
<string name="reboot_to_reset_message" msgid="2432077491101416345">"Startar om …"</string>
<string name="shutdown_progress" msgid="2281079257329981203">"Avslutar…"</string>
<string name="shutdown_confirm" product="tablet" msgid="3385745179555731470">"Din surfplatta stängs av."</string>
- <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"Tv:n stängs av."</string>
+ <string name="shutdown_confirm" product="tv" msgid="476672373995075359">"TV:n stängs av."</string>
<string name="shutdown_confirm" product="watch" msgid="3490275567476369184">"Klockan stängs av."</string>
<string name="shutdown_confirm" product="default" msgid="649792175242821353">"Din telefon stängs av."</string>
<string name="shutdown_confirm_question" msgid="2906544768881136183">"Vill du stänga av?"</string>
@@ -276,7 +276,7 @@
<string name="permdesc_sendSms" msgid="7094729298204937667">"Tillåter att appen skickar SMS. Detta kan leda till oväntade avgifter. Skadliga appar kan skicka meddelanden utan ditt godkännande vilket kan kosta pengar."</string>
<string name="permlab_readSms" msgid="8745086572213270480">"läsa dina textmeddelanden (SMS eller MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="2467981548684735522">"Tillåter att appen läser SMS som sparats på surfplattan eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess."</string>
- <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Tillåter att appen läser sms som har sparats på tv:n eller SIM-kortet. På så sätt kan appen läsa alla sms oavsett innehåll eller sekretess."</string>
+ <string name="permdesc_readSms" product="tv" msgid="5102425513647038535">"Tillåter att appen läser sms som har sparats på TV:n eller SIM-kortet. På så sätt kan appen läsa alla sms oavsett innehåll eller sekretess."</string>
<string name="permdesc_readSms" product="default" msgid="3695967533457240550">"Tillåter att appen läser SMS som sparats på mobilen eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess."</string>
<string name="permlab_receiveWapPush" msgid="5991398711936590410">"ta emot textmeddelanden (WAP)"</string>
<string name="permdesc_receiveWapPush" msgid="748232190220583385">"Tillåter att appen tar emot och hanterar WAP-meddelanden. Med den här behörigheten kan appen övervaka eller ta bort meddelanden som skickats till dig utan att visa dem för dig."</string>
@@ -294,7 +294,7 @@
<string name="permdesc_systemAlertWindow" msgid="8584678381972820118">"Tillåter att appen att dras ovanpå andra appar eller delar av användargränssnittet. De kan störa din användning av gränssnittet i olika appar eller ändra vad du tror visas i andra appar."</string>
<string name="permlab_persistentActivity" msgid="8841113627955563938">"se till att appen alltid körs"</string>
<string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör surfplattan långsam."</string>
- <string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Tillåter att en app gör vissa delar beständiga i minnet. Det kan begränsa mängden minne som är tillgänglig för andra appar och gör tv:n långsammare."</string>
+ <string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Tillåter att en app gör vissa delar beständiga i minnet. Det kan begränsa mängden minne som är tillgänglig för andra appar och gör TV:n långsammare."</string>
<string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör mobilen långsam."</string>
<string name="permlab_getPackageSize" msgid="7472921768357981986">"mäta appens lagringsplats"</string>
<string name="permdesc_getPackageSize" msgid="3921068154420738296">"Tillåter att appen hämtar kod, data och cachestorlekar"</string>
@@ -306,33 +306,33 @@
<string name="permdesc_receiveBootCompleted" product="default" msgid="513950589102617504">"Tillåter att appen startar automatiskt när systemet har startats om. Detta kan innebära att det tar längre tid att starta mobilen och att mobilen blir långsammare i och med att appen hela tiden körs i bakgrunden."</string>
<string name="permlab_broadcastSticky" msgid="7919126372606881614">"Skicka sticky broadcast"</string>
<string name="permdesc_broadcastSticky" product="tablet" msgid="7749760494399915651">"Tillåter att appen skickar sticky broadcasts, som finns kvar när sändningen är slut. Vid intensiv användning kan mobilen bli långsam eller instabil eftersom minnet överbelastas."</string>
- <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Tillåter att appen skickar sticky broadcasts som finns kvar när sändningen är slut. Överdriven användning kan göra tv:n seg eller instabil eftersom den använder för mycket minne."</string>
+ <string name="permdesc_broadcastSticky" product="tv" msgid="6839285697565389467">"Tillåter att appen skickar sticky broadcasts som finns kvar när sändningen är slut. Överdriven användning kan göra TV:n seg eller instabil eftersom den använder för mycket minne."</string>
<string name="permdesc_broadcastSticky" product="default" msgid="2825803764232445091">"Tillåter att appen skickar sticky broadcast, som finns kvar när sändningen är slut. Vid intensiv användning kan mobilen bli långsam eller instabil eftersom minnet överbelastas."</string>
<string name="permlab_readContacts" msgid="8348481131899886131">"läsa dina kontakter"</string>
<string name="permdesc_readContacts" product="tablet" msgid="5294866856941149639">"Tillåter att appen läser kontaktuppgifter som sparats på surfplattan, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string>
- <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Tillåter att appen läser data om dina kontakter som sparats på tv:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med enskilda individer. Med den här behörigheten kan appar spara dina kontaktuppgifter och skadliga appar kan dela kontaktuppgifter utan att du vet om det."</string>
+ <string name="permdesc_readContacts" product="tv" msgid="1839238344654834087">"Tillåter att appen läser data om dina kontakter som sparats på TV:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med enskilda individer. Med den här behörigheten kan appar spara dina kontaktuppgifter och skadliga appar kan dela kontaktuppgifter utan att du vet om det."</string>
<string name="permdesc_readContacts" product="default" msgid="8440654152457300662">"Tillåter att appen läser kontaktuppgifter som sparats på mobilen, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string>
<string name="permlab_writeContacts" msgid="5107492086416793544">"ändra kontakterna"</string>
<string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"Tillåter att appen ändrar kontaktuppgifter som sparats på surfplattan, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter."</string>
- <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Tillåter att appen ändrar uppgifterna om dina kontakter som har sparats på tv:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med särskilda kontakter. Med den här behörigheten kan appar ta bort kontaktuppgifter."</string>
+ <string name="permdesc_writeContacts" product="tv" msgid="5438230957000018959">"Tillåter att appen ändrar uppgifterna om dina kontakter som har sparats på TV:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med särskilda kontakter. Med den här behörigheten kan appar ta bort kontaktuppgifter."</string>
<string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"Tillåter att appen ändrar kontaktuppgifter som sparats på mobilen, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter."</string>
<string name="permlab_readCallLog" msgid="3478133184624102739">"läs samtalslogg"</string>
<string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"Tillåter att appen läser pekdatorns samtalslista, inklusive uppgifter om inkommande och utgående samtal. Med den här behörigheten tillåts appen att spara samtalshistoriken. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string>
- <string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Tillåter att appen läser tv:ns samtalslista, bland annat data om inkommande och utgående samtal. Med den här behörigheten kan appar spara data i dina samtalslistor och skadliga appar kan dela data i samtalslistor utan att du vet om det."</string>
+ <string name="permdesc_readCallLog" product="tv" msgid="5611770887047387926">"Tillåter att appen läser TV:ns samtalslista, bland annat data om inkommande och utgående samtal. Med den här behörigheten kan appar spara data i dina samtalslistor och skadliga appar kan dela data i samtalslistor utan att du vet om det."</string>
<string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"Tillåter att appen läser mobilens samtalslista, inklusive uppgifter om inkommande och utgående samtal. Med den här behörigheten tillåts appen att spara samtalshistoriken. Skadliga appar kan dela uppgifterna med andra utan din vetskap."</string>
<string name="permlab_writeCallLog" msgid="8552045664743499354">"skriv samtalslogg"</string>
<string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"Tillåter att appen gör ändringar i pekdatorns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista."</string>
- <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Tillåter att appen gör ändringar i tv:ns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga appar kan använda detta för att rensa eller ändra din samtalslista."</string>
+ <string name="permdesc_writeCallLog" product="tv" msgid="4225034892248398019">"Tillåter att appen gör ändringar i TV:ns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga appar kan använda detta för att rensa eller ändra din samtalslista."</string>
<string name="permdesc_writeCallLog" product="default" msgid="683941736352787842">"Tillåter att appen gör ändringar i mobilens samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista."</string>
<string name="permlab_bodySensors" msgid="4871091374767171066">"kroppssens. (för hjärtat m.m.)"</string>
<string name="permdesc_bodySensors" product="default" msgid="4380015021754180431">"Ger appen åtkomst till information från sensorer om ditt fysiska tillstånd, till exempel din puls."</string>
<string name="permlab_readCalendar" msgid="5972727560257612398">"läsa kalenderuppgifter plus konfidentiell information"</string>
<string name="permdesc_readCalendar" product="tablet" msgid="4216462049057658723">"Tillåter att appen läser alla kalenderuppgifter som sparats på surfplattan, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga."</string>
- <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Tillåter att appen läser alla kalenderhändelser som sparats på tv:n, bland annat de som tillhör vänner eller kollegor. På så sätt kan appen dela eller spara dina kalenderhändelser oavsett sekretess eller känslighet."</string>
+ <string name="permdesc_readCalendar" product="tv" msgid="3191352452242394196">"Tillåter att appen läser alla kalenderhändelser som sparats på TV:n, bland annat de som tillhör vänner eller kollegor. På så sätt kan appen dela eller spara dina kalenderhändelser oavsett sekretess eller känslighet."</string>
<string name="permdesc_readCalendar" product="default" msgid="7434548682470851583">"Tillåter att appen läser alla kalenderuppgifter som sparats på mobilen, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga."</string>
<string name="permlab_writeCalendar" msgid="8438874755193825647">"lägga till eller ändra kalenderuppgifter och skicka e-post till gäster utan ägarens vetskap"</string>
<string name="permdesc_writeCalendar" product="tablet" msgid="6679035520113668528">"Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på surfplattan, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Tillåter att appen lägger till, tar bort och ändrar händelser som du kan ändra på tv:n, inklusive dina vänners eller kollegors uppgifter. Appen kan på så sätt skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra uppgifter utan ägarens vetskap."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="1273290605500902507">"Tillåter att appen lägger till, tar bort och ändrar händelser som du kan ändra på TV:n, inklusive dina vänners eller kollegors uppgifter. Appen kan på så sätt skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra uppgifter utan ägarens vetskap."</string>
<string name="permdesc_writeCalendar" product="default" msgid="2324469496327249376">"Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på mobilen, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få åtkomst till extra kommandon för platsleverantör"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="6078307221056649927">"Tillåter att appen får åtkomst till extra kommandon för platsleverantör. Detta kan innebära att appen tillåts störa funktionen för GPS eller andra platskällor."</string>
@@ -359,14 +359,14 @@
<string name="permlab_readPhoneState" msgid="9178228524507610486">"läsa telefonens status och identitet"</string>
<string name="permdesc_readPhoneState" msgid="1639212771826125528">"Tillåter att appen kommer åt enhetens telefonfunktioner. Med den här behörigheten tillåts appen att identifiera mobilens telefonnummer och enhets-ID, om ett samtal pågår och vilket nummer samtalet är kopplat till."</string>
<string name="permlab_wakeLock" product="tablet" msgid="1531731435011495015">"förhindra att surfplattan går in i viloläge"</string>
- <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"förhindra att tv:n försätts i viloläge"</string>
+ <string name="permlab_wakeLock" product="tv" msgid="2601193288949154131">"förhindra att TV:n försätts i viloläge"</string>
<string name="permlab_wakeLock" product="default" msgid="573480187941496130">"förhindra att telefonen sätts i viloläge"</string>
<string name="permdesc_wakeLock" product="tablet" msgid="7311319824400447868">"Tillåter att appen förhindrar att surfplattan går in i viloläge."</string>
- <string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"Tillåter att appen förhindrar att tv:n försätts i viloläge."</string>
+ <string name="permdesc_wakeLock" product="tv" msgid="3208534859208996974">"Tillåter att appen förhindrar att TV:n försätts i viloläge."</string>
<string name="permdesc_wakeLock" product="default" msgid="8559100677372928754">"Tillåter att appen förhindrar att mobilen går in i viloläge."</string>
<string name="permlab_transmitIr" msgid="7545858504238530105">"tillåt IR-sändning"</string>
<string name="permdesc_transmitIr" product="tablet" msgid="5358308854306529170">"Tillåter att appen använder surfplattans IR-sändare."</string>
- <string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"Tillåter att appen använder den infraröda sändaren på tv:n."</string>
+ <string name="permdesc_transmitIr" product="tv" msgid="3926790828514867101">"Tillåter att appen använder den infraröda sändaren på TV:n."</string>
<string name="permdesc_transmitIr" product="default" msgid="7957763745020300725">"Tillåter att appen använder mobilens IR-sändare."</string>
<string name="permlab_setWallpaper" msgid="6627192333373465143">"ange bakgrund"</string>
<string name="permdesc_setWallpaper" msgid="7373447920977624745">"Tillåter att appen anger systemets bakgrund."</string>
@@ -374,11 +374,11 @@
<string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"Tillåter att appen ger tips om systemets bakgrundsstorlek."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"ange tidszon"</string>
<string name="permdesc_setTimeZone" product="tablet" msgid="1676983712315827645">"Tillåter att appen ändrar pekdatorns tidszon."</string>
- <string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"Tillåter att appen ändrar tidszonen på tv:n."</string>
+ <string name="permdesc_setTimeZone" product="tv" msgid="888864653946175955">"Tillåter att appen ändrar tidszonen på TV:n."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4499943488436633398">"Tillåter att appen ändrar mobilens tidszon."</string>
<string name="permlab_getAccounts" msgid="1086795467760122114">"hitta konton på enheten"</string>
<string name="permdesc_getAccounts" product="tablet" msgid="2741496534769660027">"Tillåter att appen hämtar en lista över alla kända konton på surfplattan. Detta kan inkludera konton som har skapats av appar som du har installerat."</string>
- <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Tillåter att appen hämtar listan med konton som tv:n kan identifiera. Den kan innehålla konton som skapats av appar som du har installerat."</string>
+ <string name="permdesc_getAccounts" product="tv" msgid="4190633395633907543">"Tillåter att appen hämtar listan med konton som TV:n kan identifiera. Den kan innehålla konton som skapats av appar som du har installerat."</string>
<string name="permdesc_getAccounts" product="default" msgid="3448316822451807382">"Tillåter att appen hämtar en lista över alla kända konton på mobilen. Detta kan inkludera konton som har skapats av appar som du har installerat."</string>
<string name="permlab_accessNetworkState" msgid="4951027964348974773">"visa nätverksanslutningar"</string>
<string name="permdesc_accessNetworkState" msgid="8318964424675960975">"Tillåter att appen kommer åt information om nätverksanslutningarna, till exempel vilka nätverk som finns och är anslutna."</string>
@@ -394,21 +394,21 @@
<string name="permdesc_changeWifiState" msgid="7137950297386127533">"Tillåter att appen ansluter till och kopplar från Wi-Fi-åtkomstpunkter samt gör ändringar i enhetens konfiguration för Wi-Fi-nätverk."</string>
<string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"tillåt Wi-Fi multicast-mottagning"</string>
<string name="permdesc_changeWifiMulticastState" product="tablet" msgid="7969774021256336548">"Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här surfplattan. Detta drar mer batteri än när multicastläget inte används."</string>
- <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Tillåter att appen tar emot paket som skickats till alla enheter i ett Wi-Fi-nätverk med multicastadress, inte bara till tv:n. Detta drar mer batteri än när multicastläget inte används."</string>
+ <string name="permdesc_changeWifiMulticastState" product="tv" msgid="9031975661145014160">"Tillåter att appen tar emot paket som skickats till alla enheter i ett Wi-Fi-nätverk med multicastadress, inte bara till TV:n. Detta drar mer batteri än när multicastläget inte används."</string>
<string name="permdesc_changeWifiMulticastState" product="default" msgid="6851949706025349926">"Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här mobilen. Detta drar mer batteri än när multicastläget inte används."</string>
<string name="permlab_bluetoothAdmin" msgid="6006967373935926659">"få åtkomst till Bluetooth-inställningar"</string>
<string name="permdesc_bluetoothAdmin" product="tablet" msgid="6921177471748882137">"Tillåter att appen konfigurerar den lokala Bluetooth-surfplattan samt upptäcker och parkopplar den med fjärranslutna enheter."</string>
- <string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Tillåter att appen konfigurerar den lokala Bluetooth-tv:n och identifierar och kopplar den till fjärrenheter."</string>
+ <string name="permdesc_bluetoothAdmin" product="tv" msgid="3373125682645601429">"Tillåter att appen konfigurerar den lokala Bluetooth-TV:n och identifierar och kopplar den till fjärrenheter."</string>
<string name="permdesc_bluetoothAdmin" product="default" msgid="8931682159331542137">"Tillåter att appen konfigurerar den lokala Bluetooth-mobilen samt upptäcker och parkopplar den med fjärranslutna enheter."</string>
<string name="permlab_accessWimaxState" msgid="4195907010610205703">"ansluta till och koppla från WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="6360102877261978887">"Tillåter att appen avgör om WiMAX är aktiverat och kommer åt information om eventuella anslutna WiMAX-nätverk."</string>
<string name="permlab_changeWimaxState" msgid="2405042267131496579">"ändra WiMAX-status"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="3156456504084201805">"Tillåter att appen ansluter surfplattan till eller kopplar från WiMAX-nätverk."</string>
- <string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Tillåter att appen ansluter tv:n till och kopplar från tv:n från WiMAX-nätverk."</string>
+ <string name="permdesc_changeWimaxState" product="tv" msgid="6022307083934827718">"Tillåter att appen ansluter TV:n till och kopplar från TV:n från WiMAX-nätverk."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="697025043004923798">"Tillåter att appen ansluter mobilen till eller kopplar från WiMAX-nätverk."</string>
<string name="permlab_bluetooth" msgid="6127769336339276828">"koppla till Bluetooth-enheter"</string>
<string name="permdesc_bluetooth" product="tablet" msgid="3480722181852438628">"Tillåter att appen kommer åt pekdatorns Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter."</string>
- <string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"Tillåter att appen visar konfigurationen av Bluetooth på tv:n och godkänner alla anslutningar till kopplade enheter."</string>
+ <string name="permdesc_bluetooth" product="tv" msgid="3974124940101104206">"Tillåter att appen visar konfigurationen av Bluetooth på TV:n och godkänner alla anslutningar till kopplade enheter."</string>
<string name="permdesc_bluetooth" product="default" msgid="3207106324452312739">"Tillåter att appen kommer åt mobilens Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter."</string>
<string name="permlab_nfc" msgid="4423351274757876953">"kontrollera närfältskommunikationen"</string>
<string name="permdesc_nfc" msgid="7120611819401789907">"Tillåter att appen kommunicerar med etiketter, kort och läsare för närfältskommunikation (NFC)."</string>
@@ -499,10 +499,10 @@
<string name="policydesc_limitPassword" msgid="2502021457917874968">"Styr tillåten längd och tillåtna tecken i lösenord och pinkoder för skärmlåset."</string>
<string name="policylab_watchLogin" msgid="914130646942199503">"Övervaka försök att låsa upp skärmen"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås surfplattan eller ta bort alla data från surfplattan om för många felaktiga försök görs."</string>
- <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Övervakar antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och låser tv:n eller rensar alla uppgifter på tv:n om för många felaktiga lösenord har skrivits in."</string>
+ <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Övervakar antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och låser TV:n eller rensar alla uppgifter på TV:n om för många felaktiga lösenord har skrivits in."</string>
<string name="policydesc_watchLogin" product="default" msgid="5712323091846761073">"Övervaka antalet felaktiga lösenord som angivits för skärmlåset och lås mobilen eller ta bort alla data från mobilen om för många felaktiga försök görs."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="4280246270601044505">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås surfplattan eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
- <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås tv:n eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
+ <string name="policydesc_watchLogin_secondaryUser" product="TV" msgid="3484832653564483250">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås TV:n eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="2185480427217127147">"Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås mobilen eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in."</string>
<string name="policylab_resetPassword" msgid="4934707632423915395">"Ändra skärmlåset"</string>
<string name="policydesc_resetPassword" msgid="1278323891710619128">"Ändra skärmlåset."</string>
@@ -510,11 +510,11 @@
<string name="policydesc_forceLock" msgid="1141797588403827138">"Kontrollera hur och när skärmlåset aktiveras."</string>
<string name="policylab_wipeData" msgid="3910545446758639713">"Radera all data"</string>
<string name="policydesc_wipeData" product="tablet" msgid="4306184096067756876">"Ta bort data från surfplattan utan förvarning genom att återställa standardinställningarna."</string>
- <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Rensar uppgifterna på tv:n utan föregående varning genom att återställa standardinställningarna."</string>
+ <string name="policydesc_wipeData" product="tv" msgid="5816221315214527028">"Rensar uppgifterna på TV:n utan föregående varning genom att återställa standardinställningarna."</string>
<string name="policydesc_wipeData" product="default" msgid="5096895604574188391">"Ta bort data från mobilen utan förvarning genom att återställa standardinställningarna."</string>
<string name="policylab_wipeData_secondaryUser" msgid="8362863289455531813">"Radera användaruppgifter"</string>
<string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="6336255514635308054">"Rensa användarens uppgifter på den här surfplattan utan förvarning."</string>
- <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Rensa användarens uppgifter på den här tv:n utan förvarning."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2086473496848351810">"Rensa användarens uppgifter på den här TV:n utan förvarning."</string>
<string name="policydesc_wipeData_secondaryUser" product="default" msgid="6787904546711590238">"Rensa användarens data på den här mobilen utan förvarning."</string>
<string name="policylab_setGlobalProxy" msgid="2784828293747791446">"Ange global proxyserver"</string>
<string name="policydesc_setGlobalProxy" msgid="8459859731153370499">"Ange enhetens globala proxy som ska användas när policyn aktiveras. Det är bara enhetens ägare som kan ange global proxy."</string>
@@ -663,7 +663,7 @@
<string name="faceunlock_multiple_failures" msgid="754137583022792429">"Du har försökt låsa upp med Ansiktslås för många gånger"</string>
<string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Inget SIM-kort"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"Inget SIM-kort i surfplattan."</string>
- <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Det finns inget SIM-kort i tv:n."</string>
+ <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"Det finns inget SIM-kort i TV:n."</string>
<string name="lockscreen_missing_sim_message" product="default" msgid="2186920585695169078">"Inget SIM-kort i telefonen."</string>
<string name="lockscreen_missing_sim_instructions" msgid="5372787138023272615">"Sätt i ett SIM-kort."</string>
<string name="lockscreen_missing_sim_instructions_long" msgid="3526573099019319472">"SIM-kort saknas eller kan inte läsas. Sätt i ett SIM-kort."</string>
@@ -686,13 +686,13 @@
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="2725973286239344555">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="6216672706545696955">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="9191611984625460820">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp surfplattan med din Google-inloggning.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp tv:n genom att logga in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="5316664559603394684">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp TV:n genom att logga in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="2590227559763762751">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till ombeds du att låsa upp mobilen med uppgifterna som du använder när du loggar in på Google.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="6128106399745755604">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer surfplattan att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer tv:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="950408382418270260">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
<string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="8603565142156826565">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök till kommer mobilen att återställas till fabriksinställningarna. Du förlorar då alla användardata."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="280873516493934365">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER">%d</xliff:g> gånger. Tv:n kommer nu att återställas till standardinställningarna."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="3195755534096192191">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER">%d</xliff:g> gånger. TV:n kommer nu att återställas till standardinställningarna."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="3025504721764922246">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Glömt ditt grafiska lösenord?"</string>
@@ -778,7 +778,7 @@
<string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Tillåter att appen läser historiken för besökta sidor och alla bokmärken i webbläsaren. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string>
<string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"skriva bokmärken och historik på webben"</string>
<string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på surfplattan. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string>
- <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Tillåter att appen ändrar webbläsarens historik eller bokmärken som har sparats på tv:n. Appen kan därmed rensa eller ändra uppgifter i webbläsaren. Obs! Den här behörigheten kanske inte gäller för webbläsare från tredje part eller andra appar med webbfunktioner."</string>
+ <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Tillåter att appen ändrar webbläsarens historik eller bokmärken som har sparats på TV:n. Appen kan därmed rensa eller ändra uppgifter i webbläsaren. Obs! Den här behörigheten kanske inte gäller för webbläsare från tredje part eller andra appar med webbfunktioner."</string>
<string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på telefonen. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion."</string>
<string name="permlab_setAlarm" msgid="1379294556362091814">"ställa in ett alarm"</string>
<string name="permdesc_setAlarm" msgid="316392039157473848">"Tillåter att appen ställer in ett alarm i en befintlig alarmapp. Vissa alarmappar har inte den här funktionen."</string>
@@ -988,7 +988,7 @@
<string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ange den obligatoriska PIN-koden:"</string>
<string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kod:"</string>
<string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Surfplattans Wi-Fi-anslutning kommer tillfälligt att avbrytas när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
- <string name="wifi_p2p_frequency_conflict_message" product="tv" msgid="3087858235069421128">"Tv:n kopplas tillfälligt från Wi-Fi-nätverket när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="wifi_p2p_frequency_conflict_message" product="tv" msgid="3087858235069421128">"TV:n kopplas tillfälligt från Wi-Fi-nätverket när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Mobilen kommer tillfälligt att kopplas från Wi-Fi när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="select_character" msgid="3365550120617701745">"Infoga tecken"</string>
<string name="sms_control_title" msgid="7296612781128917719">"Skickar SMS"</string>
@@ -1289,13 +1289,13 @@
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7813713389422226531">"Du har angett fel lösenord <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="74089475965050805">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. \n\nFörsök igen om <xliff:g id="NUMBER_1">%d</xliff:g> sekunder."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1575557200627128949">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs surfplattan till fabriksinställningarna. Du förlorar då alla användardata."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer tv:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="5621231220154419413">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="4051015943038199910">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök återställs mobilen till fabriksinställningarna. Du förlorar då alla användardata."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2072996269148483637">"Du har försökt låsa upp surfplattan på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Surfplattan återställs nu till fabriksinställningarna."</string>
- <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Du har misslyckats med att låsa upp tv:n <xliff:g id="NUMBER">%d</xliff:g> gånger. Tv:n kommer nu att återställas till standardinställningarna."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tv" msgid="4987878286750741463">"Du har misslyckats med att låsa upp TV:n <xliff:g id="NUMBER">%d</xliff:g> gånger. TV:n kommer nu att återställas till standardinställningarna."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="4817627474419471518">"Du har försökt låsa upp mobilen på fel sätt <xliff:g id="NUMBER">%d</xliff:g> gånger. Mobilen återställs nu till fabriksinställningarna."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="3253575572118914370">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp surfplattan med ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
- <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp tv:n via ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4224651132862313471">"Du har ritat fel mönster för upplåsning <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> misslyckade försök blir du ombedd att låsa upp TV:n via ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto.\n\n Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
<string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ta bort"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index d367887275ac..af04a46a69a3 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -589,7 +589,7 @@
<string name="phoneTypeTelex" msgid="3367879952476250512">"Teleksi"</string>
<string name="phoneTypeTtyTdd" msgid="8606514378585000044">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Nambari ya Simu ya Mkononi ya Kazini"</string>
- <string name="phoneTypeWorkPager" msgid="649938731231157056">"Kiunda ujumbe cha Kazini"</string>
+ <string name="phoneTypeWorkPager" msgid="649938731231157056">"Peja ya Kazini"</string>
<string name="phoneTypeAssistant" msgid="5596772636128562884">"Msaidizi"</string>
<string name="phoneTypeMms" msgid="7254492275502768992">"MMS"</string>
<string name="eventTypeCustom" msgid="7837586198458073404">"Maalum"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 439b9f284e76..83b0d1813fde 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -530,8 +530,8 @@
<item msgid="8901098336658710359">"Nhà riêng"</item>
<item msgid="869923650527136615">"Di Động"</item>
<item msgid="7897544654242874543">"Cơ quan"</item>
- <item msgid="1103601433382158155">"Số fax Cơ quan"</item>
- <item msgid="1735177144948329370">"Số fax Nhà riêng"</item>
+ <item msgid="1103601433382158155">"Số fax cơ quan"</item>
+ <item msgid="1735177144948329370">"Số fax nhà riêng"</item>
<item msgid="603878674477207394">"Số máy nhắn tin"</item>
<item msgid="1650824275177931637">"Khác"</item>
<item msgid="9192514806975898961">"Tùy chỉnh"</item>
@@ -573,8 +573,8 @@
<string name="phoneTypeHome" msgid="2570923463033985887">"Nhà riêng"</string>
<string name="phoneTypeMobile" msgid="6501463557754751037">"Di Động"</string>
<string name="phoneTypeWork" msgid="8863939667059911633">"Cơ quan"</string>
- <string name="phoneTypeFaxWork" msgid="3517792160008890912">"Số fax Cơ quan"</string>
- <string name="phoneTypeFaxHome" msgid="2067265972322971467">"Số fax Nhà riêng"</string>
+ <string name="phoneTypeFaxWork" msgid="3517792160008890912">"Số fax cơ quan"</string>
+ <string name="phoneTypeFaxHome" msgid="2067265972322971467">"Số fax nhà riêng"</string>
<string name="phoneTypePager" msgid="7582359955394921732">"Số máy nhắn tin"</string>
<string name="phoneTypeOther" msgid="1544425847868765990">"Khác"</string>
<string name="phoneTypeCallback" msgid="2712175203065678206">"Số gọi lại"</string>
@@ -586,8 +586,8 @@
<string name="phoneTypeRadio" msgid="4093738079908667513">"Radio"</string>
<string name="phoneTypeTelex" msgid="3367879952476250512">"Số telex"</string>
<string name="phoneTypeTtyTdd" msgid="8606514378585000044">"TTY TDD"</string>
- <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Số điện thoại di động tại Cơ quan"</string>
- <string name="phoneTypeWorkPager" msgid="649938731231157056">"Số Máy nhắn tin tại Cơ quan"</string>
+ <string name="phoneTypeWorkMobile" msgid="1311426989184065709">"Di động tại cơ quan"</string>
+ <string name="phoneTypeWorkPager" msgid="649938731231157056">"Số máy nhắn tin cơ quan"</string>
<string name="phoneTypeAssistant" msgid="5596772636128562884">"Số điện thoại Hỗ trợ"</string>
<string name="phoneTypeMms" msgid="7254492275502768992">"MMS"</string>
<string name="eventTypeCustom" msgid="7837586198458073404">"Tùy chỉnh"</string>
diff --git a/core/res/res/values/colors_holo.xml b/core/res/res/values/colors_holo.xml
index 9d1dda547586..917c781812ca 100644
--- a/core/res/res/values/colors_holo.xml
+++ b/core/res/res/values/colors_holo.xml
@@ -78,14 +78,14 @@
<!-- Forward compatibility for Material-style theme colors -->
<eat-comment />
- <color name="holo_primary_dark">#ff000000</color>
+ <color name="holo_primary_dark">@color/black</color>
<color name="holo_primary">#ff222222</color>
<color name="holo_control_activated">@color/holo_blue_light</color>
<color name="holo_control_normal">#39cccccc</color>
<color name="holo_button_pressed">#59f0f0f0</color>
<color name="holo_button_normal">#bd292f34</color>
- <color name="holo_light_primary_dark">#ff000000</color>
+ <color name="holo_light_primary_dark">@color/black</color>
<color name="holo_light_primary">#ffe6e6e6</color>
<color name="holo_light_control_activated">@color/holo_blue_light</color>
<color name="holo_light_control_normal">#dacccccc</color>
diff --git a/core/res/res/values/colors_legacy.xml b/core/res/res/values/colors_legacy.xml
index ad22845174e4..a3ce6524bdcb 100644
--- a/core/res/res/values/colors_legacy.xml
+++ b/core/res/res/values/colors_legacy.xml
@@ -17,6 +17,12 @@
<!-- Colors specific to pre-Holo themes. -->
<resources>
+ <!-- A bright Android-y green -->
+ <color name="legacy_green">#ff90df25</color>
+
+ <!-- A bright orange suitable for use in the early 2000s -->
+ <color name="legacy_orange">#fffea50b</color>
+
<!-- Highlight colors for the legacy themes -->
<eat-comment />
@@ -27,19 +33,17 @@
<!-- Forward compatibility for Material-style theme colors -->
<eat-comment />
- <color name="legacy_primary_dark">#ff000000</color>
- <color name="legacy_primary">#ffe6e6e6</color>
- <color name="legacy_primary_light">#ffffffff</color>
- <color name="legacy_control_activated">#ff90df25</color>
+ <color name="legacy_primary_dark">@color/black</color>
+ <color name="legacy_primary">#ff222222</color>
+ <color name="legacy_control_activated">@color/legacy_green</color>
<color name="legacy_control_normal">#99ffffff</color>
- <color name="legacy_button_pressed">#fffea50b</color>
+ <color name="legacy_button_pressed">@color/legacy_orange</color>
<color name="legacy_button_normal">#f3dbdbdb</color>
- <color name="legacy_light_primary_dark">@color/legacy_primary_dark</color>
- <color name="legacy_light_primary">@color/legacy_primary</color>
- <color name="legacy_light_primary_light">@color/legacy_primary_light</color>
- <color name="legacy_light_control_activated">@color/legacy_control_activated</color>
+ <color name="legacy_light_primary_dark">@color/black</color>
+ <color name="legacy_light_primary">#ffe6e6e6</color>
+ <color name="legacy_light_control_activated">@color/legacy_green</color>
<color name="legacy_light_control_normal">#99000000</color>
- <color name="legacy_light_button_pressed">@color/legacy_button_pressed</color>
- <color name="legacy_light_button_normal">@color/legacy_button_normal</color>
+ <color name="legacy_light_button_pressed">@color/legacy_orange</color>
+ <color name="legacy_light_button_normal">#f3dbdbdb</color>
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 38a1693141dc..58640eb2ef55 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -307,9 +307,10 @@ please see styles_device_defaults.xml.
<style name="TextAppearance.Material.Widget.PopupMenu"/>
<style name="TextAppearance.Material.Widget.PopupMenu.Large" parent="TextAppearance.Material.Menu" />
<style name="TextAppearance.Material.Widget.PopupMenu.Small" parent="TextAppearance.Material.Menu" />
- <style name="TextAppearance.Material.Widget.PopupMenu.Header" parent="TextAppearance.Material.Subhead">
+ <style name="TextAppearance.Material.Widget.PopupMenu.Header">
<item name="fontFamily">@string/font_family_title_material</item>
<item name="textSize">@dimen/text_size_menu_header_material</item>
+ <item name="textColor">?attr/textColorSecondary</item>
</style>
<style name="TextAppearance.Material.Widget.DropDownHint" parent="TextAppearance.Material.Menu" />
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index 075ceaa66c16..b6b4f4fa96f9 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -48,6 +48,11 @@ public class LinkPropertiesTest extends TestCase {
private static LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
private static LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
+ // TODO: replace all calls to NetworkUtils.numericToInetAddress with calls to this method.
+ private InetAddress Address(String addrString) {
+ return NetworkUtils.numericToInetAddress(addrString);
+ }
+
public void assertLinkPropertiesEqual(LinkProperties source, LinkProperties target) {
// Check implementation of equals(), element by element.
assertTrue(source.isIdenticalInterfaceName(target));
@@ -647,5 +652,26 @@ public class LinkPropertiesTest extends TestCase {
assertTrue(v6lp.isReachable(kLinkLocalDnsWithScope));
assertTrue(v6lp.isReachable(kOnLinkDns));
assertTrue(v6lp.isReachable(DNS6));
+
+ // Check isReachable on stacked links. This requires that the source IP address be assigned
+ // on the interface returned by the route lookup.
+ LinkProperties stacked = new LinkProperties();
+
+ // Can't add a stacked link without an interface name.
+ stacked.setInterfaceName("v4-test0");
+ v6lp.addStackedLink(stacked);
+
+ InetAddress stackedAddress = Address("192.0.0.4");
+ LinkAddress stackedLinkAddress = new LinkAddress(stackedAddress, 32);
+ assertFalse(v6lp.isReachable(stackedAddress));
+ stacked.addLinkAddress(stackedLinkAddress);
+ assertFalse(v6lp.isReachable(stackedAddress));
+ stacked.addRoute(new RouteInfo(stackedLinkAddress));
+ assertTrue(stacked.isReachable(stackedAddress));
+ assertTrue(v6lp.isReachable(stackedAddress));
+
+ assertFalse(v6lp.isReachable(DNS1));
+ stacked.addRoute(new RouteInfo((IpPrefix) null, stackedAddress));
+ assertTrue(v6lp.isReachable(DNS1));
}
}
diff --git a/core/tests/coretests/src/android/util/OrientationUtil.java b/core/tests/coretests/src/android/util/OrientationUtil.java
new file mode 100644
index 000000000000..ecdca5d608c4
--- /dev/null
+++ b/core/tests/coretests/src/android/util/OrientationUtil.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.util;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.pm.ActivityInfo;
+
+import android.test.ActivityInstrumentationTestCase2;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Utilities for manipulating screen orientation.
+ */
+public final class OrientationUtil {
+
+ private final Activity mActivity;
+ private final Instrumentation mInstrumentation;
+
+ private final Runnable mSetToPortrait = new Runnable() {
+ @Override
+ public void run() {
+ mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+ };
+
+ private final Runnable mSetToLandscape = new Runnable() {
+ @Override
+ public void run() {
+ mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ }
+ };
+
+ public static OrientationUtil initializeAndStartActivityIfNotStarted(
+ ActivityInstrumentationTestCase2 testCase) {
+ Preconditions.checkNotNull(testCase);
+ return new OrientationUtil(testCase.getActivity(), testCase.getInstrumentation());
+ }
+
+ private OrientationUtil(Activity activity, Instrumentation instrumentation) {
+ mActivity = activity;
+ mInstrumentation = instrumentation;
+ }
+
+ public void setPortraitOrientation() {
+ mInstrumentation.runOnMainSync(mSetToPortrait);
+ }
+
+ public void setLandscapeOrientation() {
+ mInstrumentation.runOnMainSync(mSetToLandscape);
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 4db1d9a56c47..6a76a27c0bd3 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -36,6 +36,7 @@ import com.android.frameworks.coretests.R;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.OrientationUtil;
import android.view.KeyEvent;
/**
@@ -43,14 +44,20 @@ import android.view.KeyEvent;
*/
public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextViewActivity>{
+ private OrientationUtil mOrientationUtil;
+
public TextViewActivityTest() {
super(TextViewActivity.class);
}
+ @Override
+ public void setUp() {
+ mOrientationUtil = OrientationUtil.initializeAndStartActivityIfNotStarted(this);
+ mOrientationUtil.setPortraitOrientation();
+ }
+
@SmallTest
public void testTypedTextIsOnScreen() throws Exception {
- getActivity();
-
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -60,8 +67,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testPositionCursorAtTextAtIndex() throws Exception {
- getActivity();
-
final String helloWorld = "Hello world!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -74,8 +79,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testLongPressToSelect() throws Exception {
- getActivity();
-
final String helloWorld = "Hello Kirk!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -87,8 +90,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testLongPressEmptySpace() throws Exception {
- getActivity();
-
final String helloWorld = "Hello big round sun!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -102,8 +103,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testLongPressAndDragToSelect() throws Exception {
- getActivity();
-
final String helloWorld = "Hello little handsome boy!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -115,8 +114,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testDoubleTapToSelect() throws Exception {
- getActivity();
-
final String helloWorld = "Hello SuetYi!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -128,8 +125,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testDoubleTapAndDragToSelect() throws Exception {
- getActivity();
-
final String helloWorld = "Hello young beautiful girl!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -141,8 +136,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testSelectBackwordsByTouch() throws Exception {
- getActivity();
-
final String helloWorld = "Hello king of the Jungle!";
onView(withId(R.id.textview)).perform(click());
onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld));
@@ -154,8 +147,6 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV
@SmallTest
public void testToolbarAppearsAfterSelection() throws Exception {
- getActivity();
-
// It'll be nice to check that the toolbar is not visible (or does not exist) here
// I can't currently find a way to do this. I'll get to it later.
diff --git a/data/keyboards/Vendor_18d1_Product_5018.kcm b/data/keyboards/Vendor_18d1_Product_5018.kcm
new file mode 100644
index 000000000000..0ca85a20d7f3
--- /dev/null
+++ b/data/keyboards/Vendor_18d1_Product_5018.kcm
@@ -0,0 +1,321 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Key character map for Google Pixel C Keyboard
+#
+
+type FULL
+
+### Basic QWERTY keys ###
+
+key A {
+ label: 'A'
+ base: 'a'
+ shift, capslock: 'A'
+}
+
+key B {
+ label: 'B'
+ base: 'b'
+ shift, capslock: 'B'
+}
+
+key C {
+ label: 'C'
+ base: 'c'
+ shift, capslock: 'C'
+ alt: '\u00e7'
+ shift+alt: '\u00c7'
+}
+
+key D {
+ label: 'D'
+ base: 'd'
+ shift, capslock: 'D'
+}
+
+key E {
+ label: 'E'
+ base: 'e'
+ shift, capslock: 'E'
+ alt: '\u0301'
+}
+
+key F {
+ label: 'F'
+ base: 'f'
+ shift, capslock: 'F'
+}
+
+key G {
+ label: 'G'
+ base: 'g'
+ shift, capslock: 'G'
+}
+
+key H {
+ label: 'H'
+ base: 'h'
+ shift, capslock: 'H'
+}
+
+key I {
+ label: 'I'
+ base: 'i'
+ shift, capslock: 'I'
+ alt: '\u0302'
+}
+
+key J {
+ label: 'J'
+ base: 'j'
+ shift, capslock: 'J'
+}
+
+key K {
+ label: 'K'
+ base: 'k'
+ shift, capslock: 'K'
+}
+
+key L {
+ label: 'L'
+ base: 'l'
+ shift, capslock: 'L'
+}
+
+key M {
+ label: 'M'
+ base: 'm'
+ shift, capslock: 'M'
+}
+
+key N {
+ label: 'N'
+ base: 'n'
+ shift, capslock: 'N'
+ alt: '\u0303'
+}
+
+key O {
+ label: 'O'
+ base: 'o'
+ shift, capslock: 'O'
+ ralt: '['
+ ralt+shift: '{'
+}
+
+key P {
+ label: 'P'
+ base: 'p'
+ shift, capslock: 'P'
+ ralt: ']'
+ ralt+shift: '}'
+}
+
+key Q {
+ label: 'Q'
+ base: 'q'
+ shift, capslock: 'Q'
+}
+
+key R {
+ label: 'R'
+ base: 'r'
+ shift, capslock: 'R'
+}
+
+key S {
+ label: 'S'
+ base: 's'
+ shift, capslock: 'S'
+ alt: '\u00df'
+}
+
+key T {
+ label: 'T'
+ base: 't'
+ shift, capslock: 'T'
+}
+
+key U {
+ label: 'U'
+ base: 'u'
+ shift, capslock: 'U'
+ alt: '\u0308'
+}
+
+key V {
+ label: 'V'
+ base: 'v'
+ shift, capslock: 'V'
+}
+
+key W {
+ label: 'W'
+ base: 'w'
+ shift, capslock: 'W'
+}
+
+key X {
+ label: 'X'
+ base: 'x'
+ shift, capslock: 'X'
+}
+
+key Y {
+ label: 'Y'
+ base: 'y'
+ shift, capslock: 'Y'
+}
+
+key Z {
+ label: 'Z'
+ base: 'z'
+ shift, capslock: 'Z'
+}
+
+key 0 {
+ label: '0'
+ base: '0'
+ shift: ')'
+}
+
+key 1 {
+ label: '1'
+ base: '1'
+ shift: '!'
+ ralt: replace ESCAPE
+}
+
+key 2 {
+ label: '2'
+ base: '2'
+ shift: '@'
+ ralt: '`'
+ ralt+shift: '~'
+}
+
+key 3 {
+ label: '3'
+ base: '3'
+ shift: '#'
+}
+
+key 4 {
+ label: '4'
+ base: '4'
+ shift: '$'
+}
+
+key 5 {
+ label: '5'
+ base: '5'
+ shift: '%'
+}
+
+key 6 {
+ label: '6'
+ base: '6'
+ shift: '^'
+ alt+shift: '\u0302'
+}
+
+key 7 {
+ label: '7'
+ base: '7'
+ shift: '&'
+}
+
+key 8 {
+ label: '8'
+ base: '8'
+ shift: '*'
+}
+
+key 9 {
+ label: '9'
+ base: '9'
+ shift: '('
+}
+
+key SPACE {
+ label: ' '
+ base: ' '
+ alt, meta: fallback SEARCH
+ ctrl: fallback LANGUAGE_SWITCH
+}
+
+key ENTER {
+ label: '\n'
+ base: '\n'
+}
+
+key TAB {
+ label: '\t'
+ base: '\t'
+}
+
+key COMMA {
+ label: ','
+ base: ','
+ shift: '<'
+}
+
+key PERIOD {
+ label: '.'
+ base: '.'
+ shift: '>'
+}
+
+key SLASH {
+ label: '/'
+ base: '/'
+ shift: '?'
+}
+
+key MINUS {
+ label: '-'
+ base: '-'
+ shift: '_'
+}
+
+key EQUALS {
+ label: '='
+ base: '='
+ shift: '+'
+ ralt: '\\'
+ ralt+shift: '|'
+}
+
+key SEMICOLON {
+ label: ';'
+ base: ';'
+ shift: ':'
+}
+
+key APOSTROPHE {
+ label: '\''
+ base: '\''
+ shift: '"'
+}
+
+### Non-printing keys ###
+
+key ESCAPE {
+ base: fallback BACK
+ alt, meta: fallback HOME
+ ctrl: fallback MENU
+}
diff --git a/data/keyboards/Vendor_18d1_Product_5018.kl b/data/keyboards/Vendor_18d1_Product_5018.kl
new file mode 100644
index 000000000000..e95ccb5c472c
--- /dev/null
+++ b/data/keyboards/Vendor_18d1_Product_5018.kl
@@ -0,0 +1,84 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Key layout for Google Pixel C Keyboard
+#
+
+# Row 1
+key 2 1
+key 3 2
+key 4 3
+key 5 4
+key 6 5
+key 7 6
+key 8 7
+key 9 8
+key 10 9
+key 11 0
+key 12 MINUS
+key 14 DEL # Backspace
+
+# Row 2
+key 15 TAB
+key 16 Q
+key 17 W
+key 18 E
+key 19 R
+key 20 T
+key 21 Y
+key 22 U
+key 23 I
+key 24 O
+key 25 P
+key 13 EQUALS
+key 28 ENTER
+
+# Row 3
+key 125 META_LEFT # "Search key"
+key 30 A
+key 31 S
+key 32 D
+key 33 F
+key 34 G
+key 35 H
+key 36 J
+key 37 K
+key 38 L
+key 39 SEMICOLON
+key 40 APOSTROPHE
+
+# Row 4
+key 42 SHIFT_LEFT
+key 44 Z
+key 45 X
+key 46 C
+key 47 V
+key 48 B
+key 49 N
+key 50 M
+key 51 COMMA
+key 52 PERIOD
+key 53 SLASH
+key 54 SHIFT_RIGHT
+
+# Row 5
+key 29 CTRL_LEFT
+key 56 ALT_LEFT
+key 57 SPACE
+key 100 ALT_RIGHT
+key 103 DPAD_UP
+key 105 DPAD_LEFT
+key 106 DPAD_RIGHT
+key 108 DPAD_DOWN
diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd
index 07ca45a957a4..c179a2e8cae4 100644
--- a/docs/html/guide/topics/resources/providing-resources.jd
+++ b/docs/html/guide/topics/resources/providing-resources.jd
@@ -646,7 +646,8 @@ application during runtime.</p>
<code>xxhdpi</code><br/>
<code>xxxhdpi</code><br/>
<code>nodpi</code><br/>
- <code>tvdpi</code>
+ <code>tvdpi</code><br/>
+ <code>anydpi</code>
</td>
<td>
<ul class="nolist">
@@ -667,7 +668,11 @@ to match the device density.</li>
<li>{@code tvdpi}: Screens somewhere between mdpi and hdpi; approximately 213dpi. This is
not considered a "primary" density group. It is mostly intended for televisions and most
apps shouldn't need it&mdash;providing mdpi and hdpi resources is sufficient for most apps and
-the system will scale them as appropriate. This qualifier was introduced with API level 13.</li>
+the system will scale them as appropriate. <em>Added in API Level 13</em></li>
+ <li>{@code anydpi}: This qualifier matches all screen densities and takes precedence over
+other qualifiers. This is useful for
+<a href="{@docRoot}training/material/drawables.html#VectorDrawables">vector drawables</a>.
+<em>Added in API Level 21</em></li>
</ul>
<p>There is a 3:4:6:8:12:16 scaling ratio between the six primary densities (ignoring the
tvdpi density). So, a 9x9 bitmap in ldpi is 12x12 in mdpi, 18x18 in hdpi, 24x24 in xhdpi and so on.
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 58de87af1a5d..11b4a9e5adf5 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -430,7 +430,7 @@ public class Paint {
* @param flags initial flag bits, as if they were passed via setFlags().
*/
public Paint(int flags) {
- mNativePaint = native_init();
+ mNativePaint = nInit();
setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS);
// TODO: Turning off hinting has undesirable side effects, we need to
// revisit hinting once we add support for subpixel positioning
@@ -448,13 +448,14 @@ public class Paint {
* new paint.
*/
public Paint(Paint paint) {
- mNativePaint = native_initWithPaint(paint.getNativeInstance());
+ mNativePaint = nInitWithPaint(paint.getNativeInstance());
setClassVariablesFrom(paint);
}
/** Restores the paint to its default settings. */
public void reset() {
- native_reset(mNativePaint);
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nReset(mNativePaint);
setFlags(HIDDEN_DEFAULT_PAINT_FLAGS);
// TODO: Turning off hinting has undesirable side effects, we need to
@@ -488,9 +489,11 @@ public class Paint {
* methods on this.
*/
public void set(Paint src) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ if (src.mNativePaint == 0) throw new NullPointerException("Source is already finalized!");
if (this != src) {
// copy over the native settings
- native_set(mNativePaint, src.mNativePaint);
+ nSet(mNativePaint, src.mNativePaint);
setClassVariablesFrom(src);
}
}
@@ -538,10 +541,11 @@ public class Paint {
* @hide
*/
public long getNativeInstance() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long newNativeShader = mShader == null ? 0 : mShader.getNativeInstance();
if (newNativeShader != mNativeShader) {
mNativeShader = newNativeShader;
- native_setShader(mNativePaint, mNativeShader);
+ nSetShader(mNativePaint, mNativeShader);
}
return mNativePaint;
}
@@ -574,26 +578,46 @@ public class Paint {
*
* @return the paint's flags (see enums ending in _Flag for bit masks)
*/
- public native int getFlags();
+ public int getFlags() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetFlags(mNativePaint);
+ }
+
+ private native int nGetFlags(long paintPtr);
/**
* Set the paint's flags. Use the Flag enum to specific flag values.
*
* @param flags The new flag bits for the paint
*/
- public native void setFlags(int flags);
+ public void setFlags(int flags) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetFlags(mNativePaint, flags);
+ }
+
+ private native void nSetFlags(long paintPtr, int flags);
/**
* Return the paint's hinting mode. Returns either
* {@link #HINTING_OFF} or {@link #HINTING_ON}.
*/
- public native int getHinting();
+ public int getHinting() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetHinting(mNativePaint);
+ }
+
+ private native int nGetHinting(long paintPtr);
/**
* Set the paint's hinting mode. May be either
* {@link #HINTING_OFF} or {@link #HINTING_ON}.
*/
- public native void setHinting(int mode);
+ public void setHinting(int mode) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetHinting(mNativePaint, mode);
+ }
+
+ private native void nSetHinting(long paintPtr, int mode);
/**
* Helper for getFlags(), returning true if ANTI_ALIAS_FLAG bit is set
@@ -615,7 +639,12 @@ public class Paint {
*
* @param aa true to set the antialias bit in the flags, false to clear it
*/
- public native void setAntiAlias(boolean aa);
+ public void setAntiAlias(boolean aa) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetAntiAlias(mNativePaint, aa);
+ }
+
+ private native void nSetAntiAlias(long paintPtr, boolean aa);
/**
* Helper for getFlags(), returning true if DITHER_FLAG bit is set
@@ -641,7 +670,12 @@ public class Paint {
*
* @param dither true to set the dithering bit in flags, false to clear it
*/
- public native void setDither(boolean dither);
+ public void setDither(boolean dither) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetDither(mNativePaint, dither);
+ }
+
+ private native void nSetDither(long paintPtr, boolean dither);
/**
* Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set
@@ -658,7 +692,12 @@ public class Paint {
* @param linearText true to set the linearText bit in the paint's flags,
* false to clear it.
*/
- public native void setLinearText(boolean linearText);
+ public void setLinearText(boolean linearText) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetLinearText(mNativePaint, linearText);
+ }
+
+ private native void nSetLinearText(long paintPtr, boolean linearText);
/**
* Helper for getFlags(), returning true if SUBPIXEL_TEXT_FLAG bit is set
@@ -675,7 +714,12 @@ public class Paint {
* @param subpixelText true to set the subpixelText bit in the paint's
* flags, false to clear it.
*/
- public native void setSubpixelText(boolean subpixelText);
+ public void setSubpixelText(boolean subpixelText) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetSubpixelText(mNativePaint, subpixelText);
+ }
+
+ private native void nSetSubpixelText(long paintPtr, boolean subpixelText);
/**
* Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
@@ -692,7 +736,12 @@ public class Paint {
* @param underlineText true to set the underlineText bit in the paint's
* flags, false to clear it.
*/
- public native void setUnderlineText(boolean underlineText);
+ public void setUnderlineText(boolean underlineText) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetUnderlineText(mNativePaint, underlineText);
+ }
+
+ private native void nSetUnderlineText(long paintPtr, boolean underlineText);
/**
* Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set
@@ -709,7 +758,12 @@ public class Paint {
* @param strikeThruText true to set the strikeThruText bit in the paint's
* flags, false to clear it.
*/
- public native void setStrikeThruText(boolean strikeThruText);
+ public void setStrikeThruText(boolean strikeThruText) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetStrikeThruText(mNativePaint, strikeThruText);
+ }
+
+ private native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
/**
* Helper for getFlags(), returning true if FAKE_BOLD_TEXT_FLAG bit is set
@@ -726,7 +780,12 @@ public class Paint {
* @param fakeBoldText true to set the fakeBoldText bit in the paint's
* flags, false to clear it.
*/
- public native void setFakeBoldText(boolean fakeBoldText);
+ public void setFakeBoldText(boolean fakeBoldText) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetFakeBoldText(mNativePaint, fakeBoldText);
+ }
+
+ private native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
/**
* Whether or not the bitmap filter is activated.
@@ -749,7 +808,12 @@ public class Paint {
* @param filter true to set the FILTER_BITMAP_FLAG bit in the paint's
* flags, false to clear it.
*/
- public native void setFilterBitmap(boolean filter);
+ public void setFilterBitmap(boolean filter) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetFilterBitmap(mNativePaint, filter);
+ }
+
+ private native void nSetFilterBitmap(long paintPtr, boolean filter);
/**
* Return the paint's style, used for controlling how primitives'
@@ -759,7 +823,8 @@ public class Paint {
* @return the paint's style setting (Fill, Stroke, StrokeAndFill)
*/
public Style getStyle() {
- return sStyleArray[native_getStyle(mNativePaint)];
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return sStyleArray[nGetStyle(mNativePaint)];
}
/**
@@ -770,7 +835,8 @@ public class Paint {
* @param style The new style to set in the paint
*/
public void setStyle(Style style) {
- native_setStyle(mNativePaint, style.nativeInt);
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetStyle(mNativePaint, style.nativeInt);
}
/**
@@ -782,7 +848,12 @@ public class Paint {
* @return the paint's color (and alpha).
*/
@ColorInt
- public native int getColor();
+ public int getColor() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetColor(mNativePaint);
+ }
+
+ private native int nGetColor(long paintPtr);
/**
* Set the paint's color. Note that the color is an int containing alpha
@@ -792,7 +863,12 @@ public class Paint {
*
* @param color The new color (including alpha) to set in the paint.
*/
- public native void setColor(@ColorInt int color);
+ public void setColor(@ColorInt int color) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetColor(mNativePaint, color);
+ }
+
+ private native void nSetColor(long paintPtr, @ColorInt int color);
/**
* Helper to getColor() that just returns the color's alpha value. This is
@@ -801,7 +877,12 @@ public class Paint {
*
* @return the alpha component of the paint's color.
*/
- public native int getAlpha();
+ public int getAlpha() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetAlpha(mNativePaint);
+ }
+
+ private native int nGetAlpha(long paintPtr);
/**
* Helper to setColor(), that only assigns the color's alpha value,
@@ -810,7 +891,12 @@ public class Paint {
*
* @param a set the alpha component [0..255] of the paint's color.
*/
- public native void setAlpha(int a);
+ public void setAlpha(int a) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetAlpha(mNativePaint, a);
+ }
+
+ private native void nSetAlpha(long paintPtr, int a);
/**
* Helper to setColor(), that takes a,r,g,b and constructs the color int
@@ -833,7 +919,12 @@ public class Paint {
* @return the paint's stroke width, used whenever the paint's style is
* Stroke or StrokeAndFill.
*/
- public native float getStrokeWidth();
+ public float getStrokeWidth() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetStrokeWidth(mNativePaint);
+ }
+
+ private native float nGetStrokeWidth(long paintPtr);
/**
* Set the width for stroking.
@@ -843,7 +934,12 @@ public class Paint {
* @param width set the paint's stroke width, used whenever the paint's
* style is Stroke or StrokeAndFill.
*/
- public native void setStrokeWidth(float width);
+ public void setStrokeWidth(float width) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetStrokeWidth(mNativePaint, width);
+ }
+
+ private native void nSetStrokeWidth(long paintPtr, float width);
/**
* Return the paint's stroke miter value. Used to control the behavior
@@ -852,7 +948,12 @@ public class Paint {
* @return the paint's miter limit, used whenever the paint's style is
* Stroke or StrokeAndFill.
*/
- public native float getStrokeMiter();
+ public float getStrokeMiter() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetStrokeMiter(mNativePaint);
+ }
+
+ private native float nGetStrokeMiter(long paintPtr);
/**
* Set the paint's stroke miter value. This is used to control the behavior
@@ -861,7 +962,12 @@ public class Paint {
* @param miter set the miter limit on the paint, used whenever the paint's
* style is Stroke or StrokeAndFill.
*/
- public native void setStrokeMiter(float miter);
+ public void setStrokeMiter(float miter) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetStrokeMiter(mNativePaint, miter);
+ }
+
+ private native void nSetStrokeMiter(long paintPtr, float miter);
/**
* Return the paint's Cap, controlling how the start and end of stroked
@@ -871,7 +977,8 @@ public class Paint {
* style is Stroke or StrokeAndFill.
*/
public Cap getStrokeCap() {
- return sCapArray[native_getStrokeCap(mNativePaint)];
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return sCapArray[nGetStrokeCap(mNativePaint)];
}
/**
@@ -881,7 +988,8 @@ public class Paint {
* style is Stroke or StrokeAndFill.
*/
public void setStrokeCap(Cap cap) {
- native_setStrokeCap(mNativePaint, cap.nativeInt);
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetStrokeCap(mNativePaint, cap.nativeInt);
}
/**
@@ -890,7 +998,8 @@ public class Paint {
* @return the paint's Join.
*/
public Join getStrokeJoin() {
- return sJoinArray[native_getStrokeJoin(mNativePaint)];
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return sJoinArray[nGetStrokeJoin(mNativePaint)];
}
/**
@@ -900,7 +1009,8 @@ public class Paint {
* Stroke or StrokeAndFill.
*/
public void setStrokeJoin(Join join) {
- native_setStrokeJoin(mNativePaint, join.nativeInt);
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetStrokeJoin(mNativePaint, join.nativeInt);
}
/**
@@ -915,7 +1025,8 @@ public class Paint {
* drawn with a hairline (width == 0)
*/
public boolean getFillPath(Path src, Path dst) {
- return native_getFillPath(mNativePaint, src.ni(), dst.ni());
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetFillPath(mNativePaint, src.ni(), dst.ni());
}
/**
@@ -958,10 +1069,11 @@ public class Paint {
* @return filter
*/
public ColorFilter setColorFilter(ColorFilter filter) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long filterNative = 0;
if (filter != null)
filterNative = filter.native_instance;
- native_setColorFilter(mNativePaint, filterNative);
+ nSetColorFilter(mNativePaint, filterNative);
mColorFilter = filter;
return filter;
}
@@ -985,10 +1097,11 @@ public class Paint {
* @return xfermode
*/
public Xfermode setXfermode(Xfermode xfermode) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long xfermodeNative = 0;
if (xfermode != null)
xfermodeNative = xfermode.native_instance;
- native_setXfermode(mNativePaint, xfermodeNative);
+ nSetXfermode(mNativePaint, xfermodeNative);
mXfermode = xfermode;
return xfermode;
}
@@ -1012,11 +1125,12 @@ public class Paint {
* @return effect
*/
public PathEffect setPathEffect(PathEffect effect) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long effectNative = 0;
if (effect != null) {
effectNative = effect.native_instance;
}
- native_setPathEffect(mNativePaint, effectNative);
+ nSetPathEffect(mNativePaint, effectNative);
mPathEffect = effect;
return effect;
}
@@ -1041,11 +1155,12 @@ public class Paint {
* @return maskfilter
*/
public MaskFilter setMaskFilter(MaskFilter maskfilter) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long maskfilterNative = 0;
if (maskfilter != null) {
maskfilterNative = maskfilter.native_instance;
}
- native_setMaskFilter(mNativePaint, maskfilterNative);
+ nSetMaskFilter(mNativePaint, maskfilterNative);
mMaskFilter = maskfilter;
return maskfilter;
}
@@ -1072,11 +1187,12 @@ public class Paint {
* @return typeface
*/
public Typeface setTypeface(Typeface typeface) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long typefaceNative = 0;
if (typeface != null) {
typefaceNative = typeface.native_instance;
}
- native_setTypeface(mNativePaint, typefaceNative);
+ nSetTypeface(mNativePaint, typefaceNative);
mTypeface = typeface;
mNativeTypeface = typefaceNative;
return typeface;
@@ -1110,11 +1226,12 @@ public class Paint {
*/
@Deprecated
public Rasterizer setRasterizer(Rasterizer rasterizer) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
long rasterizerNative = 0;
if (rasterizer != null) {
rasterizerNative = rasterizer.native_instance;
}
- native_setRasterizer(mNativePaint, rasterizerNative);
+ nSetRasterizer(mNativePaint, rasterizerNative);
mRasterizer = rasterizer;
return rasterizer;
}
@@ -1132,7 +1249,8 @@ public class Paint {
* opaque, or the alpha from the shadow color if not.
*/
public void setShadowLayer(float radius, float dx, float dy, int shadowColor) {
- native_setShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetShadowLayer(mNativePaint, radius, dx, dy, shadowColor);
}
/**
@@ -1149,7 +1267,8 @@ public class Paint {
* @hide
*/
public boolean hasShadowLayer() {
- return native_hasShadowLayer(mNativePaint);
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nHasShadowLayer(mNativePaint);
}
/**
@@ -1161,7 +1280,8 @@ public class Paint {
* @return the paint's Align value for drawing text.
*/
public Align getTextAlign() {
- return sAlignArray[native_getTextAlign(mNativePaint)];
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return sAlignArray[nGetTextAlign(mNativePaint)];
}
/**
@@ -1173,7 +1293,8 @@ public class Paint {
* @param align set the paint's Align value for drawing text.
*/
public void setTextAlign(Align align) {
- native_setTextAlign(mNativePaint, align.nativeInt);
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetTextAlign(mNativePaint, align.nativeInt);
}
/**
@@ -1206,6 +1327,7 @@ public class Paint {
* @param locale the paint's locale value for drawing text, must not be null.
*/
public void setTextLocale(@NonNull Locale locale) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (locale == null) {
throw new IllegalArgumentException("locale cannot be null");
}
@@ -1213,7 +1335,7 @@ public class Paint {
return;
}
mLocales = new LocaleList(locale);
- native_setTextLocale(mNativePaint, locale.toString());
+ nSetTextLocale(mNativePaint, locale.toString());
}
/**
@@ -1244,13 +1366,14 @@ public class Paint {
* @param locales the paint's locale list for drawing text, must not be null or empty.
*/
public void setTextLocales(@NonNull @Size(min=1) LocaleList locales) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (locales == null || locales.isEmpty()) {
throw new IllegalArgumentException("locales cannot be null or empty");
}
if (locales.equals(mLocales)) return;
mLocales = locales;
// TODO: Pass the whole LocaleList to native code
- native_setTextLocale(mNativePaint, locales.getPrimary().toString());
+ nSetTextLocale(mNativePaint, locales.getPrimary().toString());
}
/**
@@ -1258,7 +1381,12 @@ public class Paint {
*
* @return true if elegant metrics are enabled for text drawing.
*/
- public native boolean isElegantTextHeight();
+ public boolean isElegantTextHeight() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nIsElegantTextHeight(mNativePaint);
+ }
+
+ private native boolean nIsElegantTextHeight(long paintPtr);
/**
* Set the paint's elegant height metrics flag. This setting selects font
@@ -1267,21 +1395,36 @@ public class Paint {
*
* @param elegant set the paint's elegant metrics flag for drawing text.
*/
- public native void setElegantTextHeight(boolean elegant);
+ public void setElegantTextHeight(boolean elegant) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetElegantTextHeight(mNativePaint, elegant);
+ }
+
+ private native void nSetElegantTextHeight(long paintPtr, boolean elegant);
/**
* Return the paint's text size.
*
* @return the paint's text size.
*/
- public native float getTextSize();
+ public float getTextSize() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetTextSize(mNativePaint);
+ }
+
+ private native float nGetTextSize(long paintPtr);
/**
* Set the paint's text size. This value must be > 0
*
* @param textSize set the paint's text size.
*/
- public native void setTextSize(float textSize);
+ public void setTextSize(float textSize) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetTextSize(mNativePaint, textSize);
+ }
+
+ private native void nSetTextSize(long paintPtr, float textSize);
/**
* Return the paint's horizontal scale factor for text. The default value
@@ -1289,7 +1432,12 @@ public class Paint {
*
* @return the paint's scale factor in X for drawing/measuring text
*/
- public native float getTextScaleX();
+ public float getTextScaleX() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetTextScaleX(mNativePaint);
+ }
+
+ private native float nGetTextScaleX(long paintPtr);
/**
* Set the paint's horizontal scale factor for text. The default value
@@ -1298,7 +1446,12 @@ public class Paint {
*
* @param scaleX set the paint's scale in X for drawing/measuring text.
*/
- public native void setTextScaleX(float scaleX);
+ public void setTextScaleX(float scaleX) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetTextScaleX(mNativePaint, scaleX);
+ }
+
+ private native void nSetTextScaleX(long paintPtr, float scaleX);
/**
* Return the paint's horizontal skew factor for text. The default value
@@ -1306,7 +1459,12 @@ public class Paint {
*
* @return the paint's skew factor in X for drawing text.
*/
- public native float getTextSkewX();
+ public float getTextSkewX() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetTextSkewX(mNativePaint);
+ }
+
+ private native float nGetTextSkewX(long paintPtr);
/**
* Set the paint's horizontal skew factor for text. The default value
@@ -1314,7 +1472,12 @@ public class Paint {
*
* @param skewX set the paint's skew factor in X for drawing text.
*/
- public native void setTextSkewX(float skewX);
+ public void setTextSkewX(float skewX) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetTextSkewX(mNativePaint, skewX);
+ }
+
+ private native void nSetTextSkewX(long paintPtr, float skewX);
/**
* Return the paint's letter-spacing for text. The default value
@@ -1323,7 +1486,8 @@ public class Paint {
* @return the paint's letter-spacing for drawing text.
*/
public float getLetterSpacing() {
- return native_getLetterSpacing(mNativePaint);
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetLetterSpacing(mNativePaint);
}
/**
@@ -1334,7 +1498,8 @@ public class Paint {
* @param letterSpacing set the paint's letter-spacing for drawing text.
*/
public void setLetterSpacing(float letterSpacing) {
- native_setLetterSpacing(mNativePaint, letterSpacing);
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetLetterSpacing(mNativePaint, letterSpacing);
}
/**
@@ -1355,6 +1520,7 @@ public class Paint {
* @param settings the font feature settings string to use, may be null.
*/
public void setFontFeatureSettings(String settings) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (settings != null && settings.equals("")) {
settings = null;
}
@@ -1363,7 +1529,7 @@ public class Paint {
return;
}
mFontFeatureSettings = settings;
- native_setFontFeatureSettings(mNativePaint, settings);
+ nSetFontFeatureSettings(mNativePaint, settings);
}
/**
@@ -1374,7 +1540,8 @@ public class Paint {
* @hide
*/
public int getHyphenEdit() {
- return native_getHyphenEdit(mNativePaint);
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetHyphenEdit(mNativePaint);
}
/**
@@ -1386,7 +1553,8 @@ public class Paint {
* @hide
*/
public void setHyphenEdit(int hyphen) {
- native_setHyphenEdit(mNativePaint, hyphen);
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ nSetHyphenEdit(mNativePaint, hyphen);
}
/**
@@ -1396,7 +1564,12 @@ public class Paint {
* @return the distance above (negative) the baseline (ascent) based on the
* current typeface and text size.
*/
- public native float ascent();
+ public float ascent() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nAscent(mNativePaint, mNativeTypeface);
+ }
+
+ private native float nAscent(long paintPtr, long typefacePtr);
/**
* Return the distance below (positive) the baseline (descent) based on the
@@ -1405,7 +1578,12 @@ public class Paint {
* @return the distance below (positive) the baseline (descent) based on
* the current typeface and text size.
*/
- public native float descent();
+ public float descent() {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nDescent(mNativePaint, mNativeTypeface);
+ }
+
+ private native float nDescent(long paintPtr, long typefacePtr);
/**
* Class that describes the various metrics for a font at a given text size.
@@ -1447,7 +1625,13 @@ public class Paint {
* the appropriate values given the paint's text attributes.
* @return the font's recommended interline spacing.
*/
- public native float getFontMetrics(FontMetrics metrics);
+ public float getFontMetrics(FontMetrics metrics) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics);
+ }
+
+ private native float nGetFontMetrics(long paintPtr,
+ long typefacePtr, FontMetrics metrics);
/**
* Allocates a new FontMetrics object, and then calls getFontMetrics(fm)
@@ -1487,7 +1671,13 @@ public class Paint {
*
* @return the font's interline spacing.
*/
- public native int getFontMetricsInt(FontMetricsInt fmi);
+ public int getFontMetricsInt(FontMetricsInt fmi) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi);
+ }
+
+ private native int nGetFontMetricsInt(long paintPtr,
+ long typefacePtr, FontMetricsInt fmi);
public FontMetricsInt getFontMetricsInt() {
FontMetricsInt fm = new FontMetricsInt();
@@ -1515,6 +1705,7 @@ public class Paint {
* @return The width of the text
*/
public float measureText(char[] text, int index, int count) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1526,13 +1717,13 @@ public class Paint {
return 0f;
}
if (!mHasCompatScaling) {
- return (float) Math.ceil(native_getTextAdvances(mNativePaint, mNativeTypeface, text,
+ return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text,
index, count, index, count, mBidiFlags, null, 0));
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- float w = native_getTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index,
+ float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index,
count, mBidiFlags, null, 0);
setTextSize(oldSize);
return (float) Math.ceil(w*mInvCompatScaling);
@@ -1547,6 +1738,7 @@ public class Paint {
* @return The width of the text
*/
public float measureText(String text, int start, int end) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1558,12 +1750,12 @@ public class Paint {
return 0f;
}
if (!mHasCompatScaling) {
- return (float) Math.ceil(native_getTextAdvances(mNativePaint, mNativeTypeface, text,
+ return (float) Math.ceil(nGetTextAdvances(mNativePaint, mNativeTypeface, text,
start, end, start, end, mBidiFlags, null, 0));
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- float w = native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start,
+ float w = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start,
end, mBidiFlags, null, 0);
setTextSize(oldSize);
return (float) Math.ceil(w * mInvCompatScaling);
@@ -1576,6 +1768,7 @@ public class Paint {
* @return The width of the text
*/
public float measureText(String text) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1636,6 +1829,7 @@ public class Paint {
*/
public int breakText(char[] text, int index, int count,
float maxWidth, float[] measuredWidth) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1647,20 +1841,20 @@ public class Paint {
return 0;
}
if (!mHasCompatScaling) {
- return native_breakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth,
+ return nBreakText(mNativePaint, mNativeTypeface, text, index, count, maxWidth,
mBidiFlags, measuredWidth);
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- int res = native_breakText(mNativePaint, mNativeTypeface, text, index, count,
+ int res = nBreakText(mNativePaint, mNativeTypeface, text, index, count,
maxWidth * mCompatScaling, mBidiFlags, measuredWidth);
setTextSize(oldSize);
if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
return res;
}
- private static native int native_breakText(long native_object, long native_typeface,
+ private static native int nBreakText(long nObject, long nTypeface,
char[] text, int index, int count,
float maxWidth, int bidiFlags, float[] measuredWidth);
@@ -1683,6 +1877,7 @@ public class Paint {
public int breakText(CharSequence text, int start, int end,
boolean measureForwards,
float maxWidth, float[] measuredWidth) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1731,6 +1926,7 @@ public class Paint {
*/
public int breakText(String text, boolean measureForwards,
float maxWidth, float[] measuredWidth) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1739,20 +1935,20 @@ public class Paint {
return 0;
}
if (!mHasCompatScaling) {
- return native_breakText(mNativePaint, mNativeTypeface, text, measureForwards,
+ return nBreakText(mNativePaint, mNativeTypeface, text, measureForwards,
maxWidth, mBidiFlags, measuredWidth);
}
final float oldSize = getTextSize();
setTextSize(oldSize*mCompatScaling);
- int res = native_breakText(mNativePaint, mNativeTypeface, text, measureForwards,
+ int res = nBreakText(mNativePaint, mNativeTypeface, text, measureForwards,
maxWidth*mCompatScaling, mBidiFlags, measuredWidth);
setTextSize(oldSize);
if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
return res;
}
- private static native int native_breakText(long native_object, long native_typeface,
+ private static native int nBreakText(long nObject, long nTypeface,
String text, boolean measureForwards,
float maxWidth, int bidiFlags, float[] measuredWidth);
@@ -1768,6 +1964,7 @@ public class Paint {
*/
public int getTextWidths(char[] text, int index, int count,
float[] widths) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1780,14 +1977,14 @@ public class Paint {
return 0;
}
if (!mHasCompatScaling) {
- native_getTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
+ nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
mBidiFlags, widths, 0);
return count;
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- native_getTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
+ nGetTextAdvances(mNativePaint, mNativeTypeface, text, index, count, index, count,
mBidiFlags, widths, 0);
setTextSize(oldSize);
for (int i = 0; i < count; i++) {
@@ -1851,6 +2048,7 @@ public class Paint {
* @return the number of code units in the specified text.
*/
public int getTextWidths(String text, int start, int end, float[] widths) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1865,14 +2063,14 @@ public class Paint {
return 0;
}
if (!mHasCompatScaling) {
- native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
+ nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
mBidiFlags, widths, 0);
return end - start;
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
+ nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end, start, end,
mBidiFlags, widths, 0);
setTextSize(oldSize);
for (int i = 0; i < end - start; i++) {
@@ -1904,6 +2102,7 @@ public class Paint {
int contextIndex, int contextCount, boolean isRtl, float[] advances,
int advancesIndex) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (chars == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -1920,14 +2119,14 @@ public class Paint {
return 0f;
}
if (!mHasCompatScaling) {
- return native_getTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
+ return nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
advancesIndex);
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- float res = native_getTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
+ float res = nGetTextAdvances(mNativePaint, mNativeTypeface, chars, index, count,
contextIndex, contextCount, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
advancesIndex);
setTextSize(oldSize);
@@ -1950,7 +2149,7 @@ public class Paint {
public float getTextRunAdvances(CharSequence text, int start, int end,
int contextStart, int contextEnd, boolean isRtl, float[] advances,
int advancesIndex) {
-
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2032,6 +2231,7 @@ public class Paint {
*/
public float getTextRunAdvances(String text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, float[] advances, int advancesIndex) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2048,14 +2248,14 @@ public class Paint {
}
if (!mHasCompatScaling) {
- return native_getTextAdvances(mNativePaint, mNativeTypeface, text, start, end,
+ return nGetTextAdvances(mNativePaint, mNativeTypeface, text, start, end,
contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
advancesIndex);
}
final float oldSize = getTextSize();
setTextSize(oldSize * mCompatScaling);
- float totalAdvance = native_getTextAdvances(mNativePaint, mNativeTypeface, text, start,
+ float totalAdvance = nGetTextAdvances(mNativePaint, mNativeTypeface, text, start,
end, contextStart, contextEnd, isRtl ? BIDI_FORCE_RTL : BIDI_FORCE_LTR, advances,
advancesIndex);
setTextSize(oldSize);
@@ -2096,6 +2296,7 @@ public class Paint {
*/
public int getTextRunCursor(char[] text, int contextStart, int contextLength,
int dir, int offset, int cursorOpt) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
int contextEnd = contextStart + contextLength;
if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
| (offset - contextStart) | (contextEnd - offset)
@@ -2104,7 +2305,7 @@ public class Paint {
throw new IndexOutOfBoundsException();
}
- return native_getTextRunCursor(mNativePaint, text,
+ return nGetTextRunCursor(mNativePaint, text,
contextStart, contextLength, dir, offset, cursorOpt);
}
@@ -2183,6 +2384,7 @@ public class Paint {
*/
public int getTextRunCursor(String text, int contextStart, int contextEnd,
int dir, int offset, int cursorOpt) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (((contextStart | contextEnd | offset | (contextEnd - contextStart)
| (offset - contextStart) | (contextEnd - offset)
| (text.length() - contextEnd) | cursorOpt) < 0)
@@ -2190,7 +2392,7 @@ public class Paint {
throw new IndexOutOfBoundsException();
}
- return native_getTextRunCursor(mNativePaint, text,
+ return nGetTextRunCursor(mNativePaint, text,
contextStart, contextEnd, dir, offset, cursorOpt);
}
@@ -2209,10 +2411,11 @@ public class Paint {
*/
public void getTextPath(char[] text, int index, int count,
float x, float y, Path path) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if ((index | count) < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
- native_getTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y,
+ nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, index, count, x, y,
path.ni());
}
@@ -2231,10 +2434,11 @@ public class Paint {
*/
public void getTextPath(String text, int start, int end,
float x, float y, Path path) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_getTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
+ nGetTextPath(mNativePaint, mNativeTypeface, mBidiFlags, text, start, end, x, y,
path.ni());
}
@@ -2249,13 +2453,14 @@ public class Paint {
* allocated by the caller.
*/
public void getTextBounds(String text, int start, int end, Rect bounds) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
if (bounds == null) {
throw new NullPointerException("need bounds Rect");
}
- nativeGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds);
+ nGetStringBounds(mNativePaint, mNativeTypeface, text, start, end, mBidiFlags, bounds);
}
/**
@@ -2269,13 +2474,14 @@ public class Paint {
* allocated by the caller.
*/
public void getTextBounds(char[] text, int index, int count, Rect bounds) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if ((index | count) < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
if (bounds == null) {
throw new NullPointerException("need bounds Rect");
}
- nativeGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags,
+ nGetCharArrayBounds(mNativePaint, mNativeTypeface, text, index, count, mBidiFlags,
bounds);
}
@@ -2296,7 +2502,8 @@ public class Paint {
* @return true if the typeface has a glyph for the string
*/
public boolean hasGlyph(String string) {
- return native_hasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
+ return nHasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
}
/**
@@ -2337,6 +2544,7 @@ public class Paint {
*/
public float getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd,
boolean isRtl, int offset) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2349,7 +2557,7 @@ public class Paint {
return 0.0f;
}
// TODO: take mCompatScaling into account (or eliminate compat scaling)?
- return native_getRunAdvance(mNativePaint, mNativeTypeface, text, start, end,
+ return nGetRunAdvance(mNativePaint, mNativeTypeface, text, start, end,
contextStart, contextEnd, isRtl, offset);
}
@@ -2367,6 +2575,7 @@ public class Paint {
*/
public float getRunAdvance(CharSequence text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, int offset) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2417,6 +2626,7 @@ public class Paint {
*/
public int getOffsetForAdvance(char[] text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, float advance) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2426,7 +2636,7 @@ public class Paint {
throw new IndexOutOfBoundsException();
}
// TODO: take mCompatScaling into account (or eliminate compat scaling)?
- return native_getOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end,
+ return nGetOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end,
contextStart, contextEnd, isRtl, advance);
}
@@ -2444,6 +2654,7 @@ public class Paint {
*/
public int getOffsetForAdvance(CharSequence text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, float advance) {
+ if (mNativePaint == 0) throw new NullPointerException("Already finalized!");
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
@@ -2464,90 +2675,88 @@ public class Paint {
@Override
protected void finalize() throws Throwable {
try {
- finalizer(mNativePaint);
- mNativePaint = 0;
+ if (mNativePaint != 0) {
+ nFinalizer(mNativePaint);
+ mNativePaint = 0;
+ }
} finally {
super.finalize();
}
}
- private static native long native_init();
- private static native long native_initWithPaint(long paint);
- private static native void native_reset(long native_object);
- private static native void native_set(long native_dst, long native_src);
- private static native int native_getStyle(long native_object);
- private static native void native_setStyle(long native_object, int style);
- private static native int native_getStrokeCap(long native_object);
- private static native void native_setStrokeCap(long native_object, int cap);
- private static native int native_getStrokeJoin(long native_object);
- private static native void native_setStrokeJoin(long native_object,
+ private static native long nInit();
+ private static native long nInitWithPaint(long paint);
+ private static native void nReset(long paintPtr);
+ private static native void nSet(long paintPtrDest, long paintPtrSrc);
+ private static native int nGetStyle(long paintPtr);
+ private static native void nSetStyle(long paintPtr, int style);
+ private static native int nGetStrokeCap(long paintPtr);
+ private static native void nSetStrokeCap(long paintPtr, int cap);
+ private static native int nGetStrokeJoin(long paintPtr);
+ private static native void nSetStrokeJoin(long paintPtr,
int join);
- private static native boolean native_getFillPath(long native_object,
+ private static native boolean nGetFillPath(long paintPtr,
long src, long dst);
- private static native long native_setShader(long native_object, long shader);
- private static native long native_setColorFilter(long native_object,
+ private static native long nSetShader(long paintPtr, long shader);
+ private static native long nSetColorFilter(long paintPtr,
long filter);
- private static native long native_setXfermode(long native_object,
+ private static native long nSetXfermode(long paintPtr,
long xfermode);
- private static native long native_setPathEffect(long native_object,
+ private static native long nSetPathEffect(long paintPtr,
long effect);
- private static native long native_setMaskFilter(long native_object,
+ private static native long nSetMaskFilter(long paintPtr,
long maskfilter);
- private static native long native_setTypeface(long native_object,
+ private static native long nSetTypeface(long paintPtr,
long typeface);
- private static native long native_setRasterizer(long native_object,
+ private static native long nSetRasterizer(long paintPtr,
long rasterizer);
- private static native int native_getTextAlign(long native_object);
- private static native void native_setTextAlign(long native_object,
+ private static native int nGetTextAlign(long paintPtr);
+ private static native void nSetTextAlign(long paintPtr,
int align);
- private static native void native_setTextLocale(long native_object,
+ private static native void nSetTextLocale(long paintPtr,
String locale);
- private static native int native_getTextGlyphs(long native_object,
- String text, int start, int end, int contextStart, int contextEnd,
- int flags, char[] glyphs);
-
- private static native float native_getTextAdvances(long native_object, long native_typeface,
+ private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
char[] text, int index, int count, int contextIndex, int contextCount,
int bidiFlags, float[] advances, int advancesIndex);
- private static native float native_getTextAdvances(long native_object, long native_typeface,
+ private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
String text, int start, int end, int contextStart, int contextEnd,
int bidiFlags, float[] advances, int advancesIndex);
- private native int native_getTextRunCursor(long native_object, char[] text,
+ private native int nGetTextRunCursor(long paintPtr, char[] text,
int contextStart, int contextLength, int dir, int offset, int cursorOpt);
- private native int native_getTextRunCursor(long native_object, String text,
+ private native int nGetTextRunCursor(long paintPtr, String text,
int contextStart, int contextEnd, int dir, int offset, int cursorOpt);
- private static native void native_getTextPath(long native_object, long native_typeface,
+ private static native void nGetTextPath(long paintPtr, long typefacePtr,
int bidiFlags, char[] text, int index, int count, float x, float y, long path);
- private static native void native_getTextPath(long native_object, long native_typeface,
+ private static native void nGetTextPath(long paintPtr, long typefacePtr,
int bidiFlags, String text, int start, int end, float x, float y, long path);
- private static native void nativeGetStringBounds(long nativePaint, long native_typeface,
+ private static native void nGetStringBounds(long nativePaint, long typefacePtr,
String text, int start, int end, int bidiFlags, Rect bounds);
- private static native void nativeGetCharArrayBounds(long nativePaint, long native_typeface,
+ private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr,
char[] text, int index, int count, int bidiFlags, Rect bounds);
- private static native void finalizer(long nativePaint);
+ private static native void nFinalizer(long nativePaint);
- private static native void native_setShadowLayer(long native_object,
+ private static native void nSetShadowLayer(long paintPtr,
float radius, float dx, float dy, int color);
- private static native boolean native_hasShadowLayer(long native_object);
+ private static native boolean nHasShadowLayer(long paintPtr);
- private static native float native_getLetterSpacing(long native_object);
- private static native void native_setLetterSpacing(long native_object,
+ private static native float nGetLetterSpacing(long paintPtr);
+ private static native void nSetLetterSpacing(long paintPtr,
float letterSpacing);
- private static native void native_setFontFeatureSettings(long native_object,
+ private static native void nSetFontFeatureSettings(long paintPtr,
String settings);
- private static native int native_getHyphenEdit(long native_object);
- private static native void native_setHyphenEdit(long native_object, int hyphen);
- private static native boolean native_hasGlyph(long native_object, long native_typeface,
+ private static native int nGetHyphenEdit(long paintPtr);
+ private static native void nSetHyphenEdit(long paintPtr, int hyphen);
+ private static native boolean nHasGlyph(long paintPtr, long typefacePtr,
int bidiFlags, String string);
- private static native float native_getRunAdvance(long native_object, long native_typeface,
+ private static native float nGetRunAdvance(long paintPtr, long typefacePtr,
char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl,
int offset);
- private static native int native_getOffsetForAdvance(long native_object,
- long native_typeface, char[] text, int start, int end, int contextStart, int contextEnd,
+ private static native int nGetOffsetForAdvance(long paintPtr,
+ long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd,
boolean isRtl, float advance);
}
diff --git a/graphics/tests/graphicstests/src/android/graphics/PaintTest.java b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java
index b67aa7dca17b..6763dd1970ae 100644
--- a/graphics/tests/graphicstests/src/android/graphics/PaintTest.java
+++ b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java
@@ -162,4 +162,60 @@ public class PaintTest extends AndroidTestCase {
} catch (IndexOutOfBoundsException e) {
}
}
+
+ public void testMeasureTextBidi() {
+ Paint p = new Paint();
+ {
+ String bidiText = "abc \u0644\u063A\u0629 def";
+ p.setBidiFlags(Paint.BIDI_LTR);
+ float width = p.measureText(bidiText, 0, 4);
+ p.setBidiFlags(Paint.BIDI_RTL);
+ width += p.measureText(bidiText, 4, 7);
+ p.setBidiFlags(Paint.BIDI_LTR);
+ width += p.measureText(bidiText, 7, bidiText.length());
+ assertEquals(width, p.measureText(bidiText), 1.0f);
+ }
+ {
+ String bidiText = "abc \u0644\u063A\u0629 def";
+ p.setBidiFlags(Paint.BIDI_DEFAULT_LTR);
+ float width = p.measureText(bidiText, 0, 4);
+ width += p.measureText(bidiText, 4, 7);
+ width += p.measureText(bidiText, 7, bidiText.length());
+ assertEquals(width, p.measureText(bidiText), 1.0f);
+ }
+ {
+ String bidiText = "abc \u0644\u063A\u0629 def";
+ p.setBidiFlags(Paint.BIDI_FORCE_LTR);
+ float width = p.measureText(bidiText, 0, 4);
+ width += p.measureText(bidiText, 4, 7);
+ width += p.measureText(bidiText, 7, bidiText.length());
+ assertEquals(width, p.measureText(bidiText), 1.0f);
+ }
+ {
+ String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629";
+ p.setBidiFlags(Paint.BIDI_RTL);
+ float width = p.measureText(bidiText, 0, 4);
+ p.setBidiFlags(Paint.BIDI_LTR);
+ width += p.measureText(bidiText, 4, 7);
+ p.setBidiFlags(Paint.BIDI_RTL);
+ width += p.measureText(bidiText, 7, bidiText.length());
+ assertEquals(width, p.measureText(bidiText), 1.0f);
+ }
+ {
+ String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629";
+ p.setBidiFlags(Paint.BIDI_DEFAULT_RTL);
+ float width = p.measureText(bidiText, 0, 4);
+ width += p.measureText(bidiText, 4, 7);
+ width += p.measureText(bidiText, 7, bidiText.length());
+ assertEquals(width, p.measureText(bidiText), 1.0f);
+ }
+ {
+ String bidiText = "\u0644\u063A\u0629 abc \u0644\u063A\u0629";
+ p.setBidiFlags(Paint.BIDI_FORCE_RTL);
+ float width = p.measureText(bidiText, 0, 4);
+ width += p.measureText(bidiText, 4, 7);
+ width += p.measureText(bidiText, 7, bidiText.length());
+ assertEquals(width, p.measureText(bidiText), 1.0f);
+ }
+ }
}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index cc0943f52fdb..eaade34a1db6 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -209,6 +209,7 @@ endif
LOCAL_SRC_FILES += \
tests/TestContext.cpp \
+ tests/TreeContentAnimation.cpp \
tests/main.cpp
include $(BUILD_EXECUTABLE)
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index c128ca775155..eca71c6e0e8d 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -28,10 +28,21 @@ CanvasState::CanvasState(CanvasStateClient& renderer)
, mWidth(-1)
, mHeight(-1)
, mSaveCount(1)
- , mFirstSnapshot(new Snapshot)
, mCanvas(renderer)
- , mSnapshot(mFirstSnapshot) {
+ , mSnapshot(&mFirstSnapshot) {
+}
+
+CanvasState::~CanvasState() {
+ // First call freeSnapshot on all but mFirstSnapshot
+ // to invoke all the dtors
+ freeAllSnapshots();
+ // Now actually release the memory
+ while (mSnapshotPool) {
+ void* temp = mSnapshotPool;
+ mSnapshotPool = mSnapshotPool->previous;
+ free(temp);
+ }
}
void CanvasState::initializeSaveStack(
@@ -41,11 +52,12 @@ void CanvasState::initializeSaveStack(
if (mWidth != viewportWidth || mHeight != viewportHeight) {
mWidth = viewportWidth;
mHeight = viewportHeight;
- mFirstSnapshot->initializeViewport(viewportWidth, viewportHeight);
+ mFirstSnapshot.initializeViewport(viewportWidth, viewportHeight);
mCanvas.onViewportInitialized();
}
- mSnapshot = new Snapshot(mFirstSnapshot,
+ freeAllSnapshots();
+ mSnapshot = allocSnapshot(&mFirstSnapshot,
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom);
mSnapshot->fbo = mCanvas.getTargetFbo();
@@ -53,6 +65,38 @@ void CanvasState::initializeSaveStack(
mSaveCount = 1;
}
+Snapshot* CanvasState::allocSnapshot(Snapshot* previous, int savecount) {
+ void* memory;
+ if (mSnapshotPool) {
+ memory = mSnapshotPool;
+ mSnapshotPool = mSnapshotPool->previous;
+ mSnapshotPoolCount--;
+ } else {
+ memory = malloc(sizeof(Snapshot));
+ }
+ return new (memory) Snapshot(previous, savecount);
+}
+
+void CanvasState::freeSnapshot(Snapshot* snapshot) {
+ snapshot->~Snapshot();
+ // Arbitrary number, just don't let this grown unbounded
+ if (mSnapshotPoolCount > 10) {
+ free((void*) snapshot);
+ } else {
+ snapshot->previous = mSnapshotPool;
+ mSnapshotPool = snapshot;
+ mSnapshotPoolCount++;
+ }
+}
+
+void CanvasState::freeAllSnapshots() {
+ while (mSnapshot != &mFirstSnapshot) {
+ Snapshot* temp = mSnapshot;
+ mSnapshot = mSnapshot->previous;
+ freeSnapshot(temp);
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Save (layer)
///////////////////////////////////////////////////////////////////////////////
@@ -64,7 +108,7 @@ void CanvasState::initializeSaveStack(
* stack, and ensures restoreToCount() doesn't call back into subclass overrides.
*/
int CanvasState::saveSnapshot(int flags) {
- mSnapshot = new Snapshot(mSnapshot, flags);
+ mSnapshot = allocSnapshot(mSnapshot, flags);
return mSaveCount++;
}
@@ -76,14 +120,16 @@ int CanvasState::save(int flags) {
* Guaranteed to restore without side-effects.
*/
void CanvasState::restoreSnapshot() {
- sp<Snapshot> toRemove = mSnapshot;
- sp<Snapshot> toRestore = mSnapshot->previous;
+ Snapshot* toRemove = mSnapshot;
+ Snapshot* toRestore = mSnapshot->previous;
mSaveCount--;
mSnapshot = toRestore;
// subclass handles restore implementation
mCanvas.onSnapshotRestored(*toRemove, *toRestore);
+
+ freeSnapshot(toRemove);
}
void CanvasState::restore() {
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index f0fb9ba8b324..be57f44210ef 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -17,12 +17,12 @@
#ifndef ANDROID_HWUI_CANVAS_STATE_H
#define ANDROID_HWUI_CANVAS_STATE_H
+#include "Snapshot.h"
+
#include <SkMatrix.h>
#include <SkPath.h>
#include <SkRegion.h>
-#include "Snapshot.h"
-
namespace android {
namespace uirenderer {
@@ -74,6 +74,7 @@ public:
class CanvasState {
public:
CanvasState(CanvasStateClient& renderer);
+ ~CanvasState();
/**
* Initializes the first snapshot, computing the projection matrix,
@@ -157,11 +158,15 @@ public:
int getHeight() const { return mHeight; }
bool clipIsSimple() const { return currentSnapshot()->clipIsSimple(); }
- inline const Snapshot* currentSnapshot() const { return mSnapshot.get(); }
- inline Snapshot* writableSnapshot() { return mSnapshot.get(); }
- inline const Snapshot* firstSnapshot() const { return mFirstSnapshot.get(); }
+ inline const Snapshot* currentSnapshot() const { return mSnapshot; }
+ inline Snapshot* writableSnapshot() { return mSnapshot; }
+ inline const Snapshot* firstSnapshot() const { return &mFirstSnapshot; }
private:
+ Snapshot* allocSnapshot(Snapshot* previous, int savecount);
+ void freeSnapshot(Snapshot* snapshot);
+ void freeAllSnapshots();
+
/// indicates that the clip has been changed since the last time it was consumed
bool mDirtyClip;
@@ -172,13 +177,18 @@ private:
int mSaveCount;
/// Base state
- sp<Snapshot> mFirstSnapshot;
+ Snapshot mFirstSnapshot;
/// Host providing callbacks
CanvasStateClient& mCanvas;
/// Current state
- sp<Snapshot> mSnapshot;
+ Snapshot* mSnapshot;
+
+ // Pool of allocated snapshots to re-use
+ // NOTE: The dtors have already been invoked!
+ Snapshot* mSnapshotPool = nullptr;
+ int mSnapshotPoolCount = 0;
}; // class CanvasState
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index 8e7efb4e35d6..a9d1e4284d2e 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -23,14 +23,6 @@
namespace android {
namespace uirenderer {
-static bool intersect(Rect& r, const Rect& r2) {
- bool hasIntersection = r.intersect(r2);
- if (!hasIntersection) {
- r.setEmpty();
- }
- return hasIntersection;
-}
-
static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
Vertex v = {x, y};
transform.mapPoint(v.x, v.y);
@@ -67,9 +59,8 @@ bool TransformedRectangle::canSimplyIntersectWith(
return mTransform == other.mTransform;
}
-bool TransformedRectangle::intersectWith(const TransformedRectangle& other) {
- Rect translatedBounds(other.mBounds);
- return intersect(mBounds, translatedBounds);
+void TransformedRectangle::intersectWith(const TransformedRectangle& other) {
+ mBounds.doIntersect(other.mBounds);
}
bool TransformedRectangle::isEmpty() const {
@@ -146,7 +137,7 @@ Rect RectangleList::calculateBounds() const {
if (index == 0) {
bounds = tr.transformedBounds();
} else {
- bounds.intersect(tr.transformedBounds());
+ bounds.doIntersect(tr.transformedBounds());
}
}
return bounds;
@@ -275,10 +266,7 @@ void ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
if (transform->rectToRect()) {
Rect transformed(r);
transform->mapRect(transformed);
- bool hasIntersection = mClipRect.intersect(transformed);
- if (!hasIntersection) {
- mClipRect.setEmpty();
- }
+ mClipRect.doIntersect(transformed);
return;
}
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index 38fefe5ab097..f88fd92e234d 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -33,7 +33,7 @@ public:
TransformedRectangle(const Rect& bounds, const Matrix4& transform);
bool canSimplyIntersectWith(const TransformedRectangle& other) const;
- bool intersectWith(const TransformedRectangle& other);
+ void intersectWith(const TransformedRectangle& other);
bool isEmpty() const;
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index a81ffb9f59fa..0c29a9e928a2 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -44,6 +44,12 @@ namespace uirenderer {
#define DEBUG_COLOR_MERGEDBATCH 0x5f7f7fff
#define DEBUG_COLOR_MERGEDBATCH_SOLO 0x5f7fff7f
+static bool avoidOverdraw() {
+ // Don't avoid overdraw when visualizing it, since that makes it harder to
+ // debug where it's coming from, and when the problem occurs.
+ return !Properties::debugOverdraw;
+};
+
/////////////////////////////////////////////////////////////////////////////////
// Operation Batches
/////////////////////////////////////////////////////////////////////////////////
@@ -218,7 +224,10 @@ public:
// if paints are equal, then modifiers + paint attribs don't need to be compared
if (op->mPaint == mOps[0].op->mPaint) return true;
- if (op->getPaintAlpha() != mOps[0].op->getPaintAlpha()) return false;
+ if (PaintUtils::getAlphaDirect(op->mPaint)
+ != PaintUtils::getAlphaDirect(mOps[0].op->mPaint)) {
+ return false;
+ }
if (op->mPaint && mOps[0].op->mPaint &&
op->mPaint->getColorFilter() != mOps[0].op->mPaint->getColorFilter()) {
@@ -495,7 +504,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
&& mSaveStack.empty()
&& !state->mRoundRectClipState;
- if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() &&
+ if (CC_LIKELY(avoidOverdraw()) && mBatches.size() &&
state->mClipSideFlags != kClipSide_ConservativeFull &&
deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) {
// avoid overdraw by resetting drawing state + discarding drawing ops
@@ -533,7 +542,11 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
if (deferInfo.mergeable) {
// Try to merge with any existing batch with same mergeId.
- if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) {
+ std::unordered_map<mergeid_t, DrawBatch*>& mergingBatch
+ = mMergingBatches[deferInfo.batchId];
+ auto getResult = mergingBatch.find(deferInfo.mergeId);
+ if (getResult != mergingBatch.end()) {
+ targetBatch = getResult->second;
if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) {
targetBatch = nullptr;
}
@@ -577,7 +590,8 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
if (deferInfo.mergeable) {
targetBatch = new MergingDrawBatch(deferInfo,
renderer.getViewportWidth(), renderer.getViewportHeight());
- mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch);
+ mMergingBatches[deferInfo.batchId].insert(
+ std::make_pair(deferInfo.mergeId, targetBatch));
} else {
targetBatch = new DrawBatch(deferInfo);
mBatchLookup[deferInfo.batchId] = targetBatch;
@@ -642,7 +656,7 @@ void DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
// save and restore so that reordering doesn't affect final state
renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- if (CC_LIKELY(mAvoidOverdraw)) {
+ if (CC_LIKELY(avoidOverdraw())) {
for (unsigned int i = 1; i < mBatches.size(); i++) {
if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) {
discardDrawingBatches(i - 1);
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 4f2dca5f3ee1..7873fbdd342a 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -17,9 +17,10 @@
#ifndef ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
#define ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
+#include <unordered_map>
+
#include <utils/Errors.h>
#include <utils/LinearAllocator.h>
-#include <utils/TinyHashMap.h>
#include "Matrix.h"
#include "OpenGLRenderer.h"
@@ -82,8 +83,8 @@ public:
class DeferredDisplayList {
friend struct DeferStateStruct; // used to give access to allocator
public:
- DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) :
- mBounds(bounds), mAvoidOverdraw(avoidOverdraw) {
+ DeferredDisplayList(const Rect& bounds)
+ : mBounds(bounds) {
clear();
}
~DeferredDisplayList() { clear(); }
@@ -151,7 +152,6 @@ private:
// layer space bounds of rendering
Rect mBounds;
- const bool mAvoidOverdraw;
/**
* At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so
@@ -177,7 +177,7 @@ private:
* MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
* collide, which avoids the need to resolve mergeid collisions.
*/
- TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
+ std::unordered_map<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
LinearAllocator mAllocator;
};
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 38f2363f3532..70383340fc8d 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -47,7 +47,8 @@ DeferredLayerUpdater::~DeferredLayerUpdater() {
}
void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
- OpenGLRenderer::getAlphaAndModeDirect(paint, &mAlpha, &mMode);
+ mAlpha = PaintUtils::getAlphaDirect(paint);
+ mMode = PaintUtils::getXfermodeDirect(paint);
SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr;
SkRefCnt_SafeAssign(mColorFilter, colorFilter);
}
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index dc5cb8b349f1..ddfc533f9d77 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -172,10 +172,6 @@ public:
void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
bool getQuickRejected() { return mQuickRejected; }
- inline int getPaintAlpha() const {
- return OpenGLRenderer::getAlphaDirect(mPaint);
- }
-
virtual bool hasTextShadow() const {
return false;
}
@@ -213,7 +209,7 @@ protected:
if (state.mAlpha != 1.0f) return false;
- SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint);
return (mode == SkXfermode::kSrcOver_Mode ||
mode == SkXfermode::kSrc_Mode);
@@ -249,8 +245,8 @@ public:
virtual bool getLocalBounds(Rect& localBounds) override {
localBounds.set(mLocalBounds);
- OpenGLRenderer::TextShadow textShadow;
- if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) {
+ PaintUtils::TextShadow textShadow;
+ if (PaintUtils::getTextShadow(mPaint, &textShadow)) {
Rect shadow(mLocalBounds);
shadow.translate(textShadow.dx, textShadow.dx);
shadow.outset(textShadow.radius);
@@ -372,8 +368,8 @@ public:
private:
bool isSaveLayerAlpha() const {
- SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
- int alpha = OpenGLRenderer::getAlphaDirect(mPaint);
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(mPaint);
+ int alpha = PaintUtils::getAlphaDirect(mPaint);
return alpha < 255 && mode == SkXfermode::kSrcOver_Mode;
}
@@ -691,7 +687,7 @@ public:
// TODO: support clipped bitmaps by handling them in SET_TEXTURE
deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() &&
!state.mClipSideFlags &&
- OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
+ PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
(mBitmap->colorType() != kAlpha_8_SkColorType);
}
@@ -895,7 +891,7 @@ public:
deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch;
deferInfo.mergeId = getAtlasEntry(renderer) ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap;
deferInfo.mergeable = state.mMatrix.isPureTranslate() &&
- OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+ PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mBitmap->isOpaque();
}
@@ -1241,7 +1237,7 @@ public:
}
virtual bool hasTextShadow() const override {
- return OpenGLRenderer::hasTextShadow(mPaint);
+ return PaintUtils::hasTextShadow(mPaint);
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
@@ -1330,7 +1326,7 @@ public:
deferInfo.mergeable = state.mMatrix.isPureTranslate()
&& !hasDecorations
- && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+ && PaintUtils::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
}
virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override {
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 4b9d4f90675c..ccf0b48cd4be 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -667,14 +667,6 @@ bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, cons
return mDrawn;
}
-void FontRenderer::removeFont(const Font* font) {
- mActiveFonts.remove(font->getDescription());
-
- if (mCurrentFont == font) {
- mCurrentFont = nullptr;
- }
-}
-
void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
uint32_t intRadius = Blur::convertRadiusToInt(radius);
#ifdef ANDROID_ENABLE_RENDERSCRIPT
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 936c838bd6e4..8172312e9a43 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -147,8 +147,6 @@ private:
float x3, float y3, float u3, float v3,
float x4, float y4, float u4, float v4, CacheTexture* texture);
- void removeFont(const Font* font);
-
void checkTextureUpdate();
void setTextureDirty() {
diff --git a/libs/hwui/Glop.h b/libs/hwui/Glop.h
index fa20b0807a88..4785ea48cddc 100644
--- a/libs/hwui/Glop.h
+++ b/libs/hwui/Glop.h
@@ -135,10 +135,6 @@ struct Glop {
} fill;
struct Transform {
- // Orthographic projection matrix for current FBO
- // TODO: move out of Glop, since this is static per FBO
- Matrix4 ortho;
-
// modelView transform, accounting for delta between mesh transform and content of the mesh
// often represents x/y offsets within command, or scaling for mesh unit size
Matrix4 modelView;
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 69559a77c3a0..fa166ae5ca5a 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -461,11 +461,10 @@ GlopBuilder& GlopBuilder::setFillTextureLayer(Layer& layer, float alpha) {
// Transform
////////////////////////////////////////////////////////////////////////////////
-void GlopBuilder::setTransform(const Matrix4& ortho, const Matrix4& canvas,
+void GlopBuilder::setTransform(const Matrix4& canvas,
const int transformFlags) {
TRIGGER_STAGE(kTransformStage);
- mOutGlop->transform.ortho = ortho;
mOutGlop->transform.canvas = canvas;
mOutGlop->transform.transformFlags = transformFlags;
}
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 549bb21e5f8d..8d05570dd206 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -71,7 +71,7 @@ public:
GlopBuilder& setFillTextureLayer(Layer& layer, float alpha);
GlopBuilder& setTransform(const Snapshot& snapshot, const int transformFlags) {
- setTransform(snapshot.getOrthoMatrix(), *snapshot.transform, transformFlags);
+ setTransform(*snapshot.transform, transformFlags);
return *this;
}
@@ -102,8 +102,7 @@ private:
void setFill(int color, float alphaScale,
SkXfermode::Mode mode, Blend::ModeOrderSwap modeUsage,
const SkShader* shader, const SkColorFilter* colorFilter);
- void setTransform(const Matrix4& ortho, const Matrix4& canvas,
- const int transformFlags);
+ void setTransform(const Matrix4& canvas, const int transformFlags);
enum StageFlags {
kInitialStage = 0,
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 8d8528961794..f99d92b89420 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -170,7 +170,8 @@ void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right,
}
void Layer::setPaint(const SkPaint* paint) {
- OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
+ alpha = PaintUtils::getAlphaDirect(paint);
+ mode = PaintUtils::getXfermodeDirect(paint);
setColorFilter((paint) ? paint->getColorFilter() : nullptr);
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index c63b5597f284..227271d83cf8 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -58,7 +58,7 @@ void LayerRenderer::prepareDirty(int viewportWidth, int viewportHeight,
mLayer->region.clear();
dirty.set(0.0f, 0.0f, width, height);
} else {
- dirty.intersect(0.0f, 0.0f, width, height);
+ dirty.doIntersect(0.0f, 0.0f, width, height);
android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom);
mLayer->region.subtractSelf(r);
}
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index a401ce119021..cd03ac407d81 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -488,7 +488,8 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool
currentTransform()->mapRect(bounds);
// Layers only make sense if they are in the framebuffer's bounds
- if (bounds.intersect(mState.currentClipRect())) {
+ bounds.doIntersect(mState.currentClipRect());
+ if (!bounds.isEmpty()) {
// We cannot work with sub-pixels in this case
bounds.snapToPixelBoundaries();
@@ -497,23 +498,20 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool
// of the framebuffer
const Snapshot& previous = *(currentSnapshot()->previous);
Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight());
- if (!bounds.intersect(previousViewport)) {
- bounds.setEmpty();
- } else if (fboLayer) {
+
+ bounds.doIntersect(previousViewport);
+ if (!bounds.isEmpty() && fboLayer) {
clip.set(bounds);
mat4 inverse;
inverse.loadInverse(*currentTransform());
inverse.mapRect(clip);
clip.snapToPixelBoundaries();
- if (clip.intersect(untransformedBounds)) {
+ clip.doIntersect(untransformedBounds);
+ if (!clip.isEmpty()) {
clip.translate(-untransformedBounds.left, -untransformedBounds.top);
bounds.set(untransformedBounds);
- } else {
- clip.setEmpty();
}
}
- } else {
- bounds.setEmpty();
}
}
@@ -540,7 +538,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float
Rect bounds(left, top, right, bottom);
Rect clip;
calculateLayerBoundsAndClip(bounds, clip, true);
- updateSnapshotIgnoreForLayer(bounds, clip, true, getAlphaDirect(paint));
+ updateSnapshotIgnoreForLayer(bounds, clip, true, PaintUtils::getAlphaDirect(paint));
if (!mState.currentlyIgnored()) {
writableSnapshot()->resetTransform(-bounds.left, -bounds.top, 0.0f);
@@ -615,7 +613,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
Rect clip;
Rect bounds(left, top, right, bottom);
calculateLayerBoundsAndClip(bounds, clip, fboLayer);
- updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, getAlphaDirect(paint));
+ updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, PaintUtils::getAlphaDirect(paint));
// Bail out if we won't draw in this snapshot
if (mState.currentlyIgnored()) {
@@ -1038,7 +1036,8 @@ void OpenGLRenderer::dirtyLayer(const float left, const float top,
}
void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
- if (CC_LIKELY(!bounds.isEmpty() && bounds.intersect(mState.currentClipRect()))) {
+ bounds.doIntersect(mState.currentClipRect());
+ if (!bounds.isEmpty()) {
bounds.snapToPixelBoundaries();
android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom);
if (!dirty.isEmpty()) {
@@ -1112,7 +1111,8 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
// is used, it should more closely duplicate the quickReject logic (in how it uses
// snapToPixelBoundaries)
- if (!clippedBounds.intersect(currentClip)) {
+ clippedBounds.doIntersect(currentClip);
+ if (clippedBounds.isEmpty()) {
// quick rejected
return true;
}
@@ -1242,9 +1242,8 @@ void OpenGLRenderer::drawRectangleList(const RectangleList& rectangleList) {
Rect bounds = tr.getBounds();
if (transform.rectToRect()) {
transform.mapRect(bounds);
- if (!bounds.intersect(scissorBox)) {
- bounds.setEmpty();
- } else {
+ bounds.doIntersect(scissorBox);
+ if (!bounds.isEmpty()) {
handlePointNoTransform(rectangleVertices, bounds.left, bounds.top);
handlePointNoTransform(rectangleVertices, bounds.right, bounds.top);
handlePointNoTransform(rectangleVertices, bounds.left, bounds.bottom);
@@ -1405,7 +1404,7 @@ void OpenGLRenderer::renderGlop(const Glop& glop, GlopRenderType type) {
setStencilFromClip();
}
- mRenderState.render(glop);
+ mRenderState.render(glop, currentSnapshot()->getOrthoMatrix());
if (type == GlopRenderType::Standard && !mRenderState.stencil().isWriteEnabled()) {
// TODO: specify more clearly when a draw should dirty the layer.
// is writing to the stencil the only time we should ignore this?
@@ -1431,10 +1430,7 @@ void OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t
return;
}
- // Don't avoid overdraw when visualizing, since that makes it harder to
- // debug where it's coming from, and when the problem occurs.
- bool avoidOverdraw = !Properties::debugOverdraw;
- DeferredDisplayList deferredList(mState.currentClipRect(), avoidOverdraw);
+ DeferredDisplayList deferredList(mState.currentClipRect());
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
renderNode->defer(deferStruct, 0);
@@ -1958,8 +1954,8 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
FontRenderer& fontRenderer, int alpha, float x, float y) {
mCaches.textureState().activateTexture(0);
- TextShadow textShadow;
- if (!getTextShadow(paint, &textShadow)) {
+ PaintUtils::TextShadow textShadow;
+ if (!PaintUtils::getTextShadow(paint, &textShadow)) {
LOG_ALWAYS_FATAL("failed to query shadow attributes");
}
@@ -1987,8 +1983,10 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text,
renderGlop(glop);
}
+// TODO: remove this, once mState.currentlyIgnored captures snapshot alpha
bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
- float alpha = (hasTextShadow(paint) ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
+ float alpha = (PaintUtils::hasTextShadow(paint)
+ ? 1.0f : paint->getAlpha()) * currentSnapshot()->alpha;
return MathUtils::isZero(alpha)
&& PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
}
@@ -2017,11 +2015,10 @@ void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
fontRenderer.setFont(paint, SkMatrix::I());
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
+ int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
- if (CC_UNLIKELY(hasTextShadow(paint))) {
+ if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
alpha, 0.0f, 0.0f);
}
@@ -2162,13 +2159,12 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float
y = floorf(y + transform.getTranslateY() + 0.5f);
}
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
+ int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer();
- if (CC_UNLIKELY(hasTextShadow(paint))) {
+ if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) {
fontRenderer.setFont(paint, SkMatrix::I());
drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
alpha, oldX, oldY);
@@ -2238,9 +2234,8 @@ void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count,
fontRenderer.setFont(paint, SkMatrix::I());
fontRenderer.setTextureFiltering(true);
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
+ int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha;
+ SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint);
TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
const Rect* clip = &writableSnapshot()->getLocalClip();
@@ -2530,12 +2525,6 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot
renderGlop(glop);
}
-void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha,
- SkXfermode::Mode* mode) const {
- getAlphaAndModeDirect(paint, alpha, mode);
- *alpha *= currentSnapshot()->alpha;
-}
-
float OpenGLRenderer::getLayerAlpha(const Layer* layer) const {
return (layer->getAlpha() / 255.0f) * currentSnapshot()->alpha;
}
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 910af5705705..400c225b53a0 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -260,57 +260,6 @@ public:
void endMark() const;
/**
- * Gets the alpha and xfermode out of a paint object. If the paint is null
- * alpha will be 255 and the xfermode will be SRC_OVER. This method does
- * not multiply the paint's alpha by the current snapshot's alpha, and does
- * not replace the alpha with the overrideLayerAlpha
- *
- * @param paint The paint to extract values from
- * @param alpha Where to store the resulting alpha
- * @param mode Where to store the resulting xfermode
- */
- static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha,
- SkXfermode::Mode* mode) {
- *mode = getXfermodeDirect(paint);
- *alpha = getAlphaDirect(paint);
- }
-
- static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
- if (!paint) return SkXfermode::kSrcOver_Mode;
- return PaintUtils::getXfermode(paint->getXfermode());
- }
-
- static inline int getAlphaDirect(const SkPaint* paint) {
- if (!paint) return 255;
- return paint->getAlpha();
- }
-
- struct TextShadow {
- SkScalar radius;
- float dx;
- float dy;
- SkColor color;
- };
-
- static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) {
- SkDrawLooper::BlurShadowRec blur;
- if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) {
- if (textShadow) {
- textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma);
- textShadow->dx = blur.fOffset.fX;
- textShadow->dy = blur.fOffset.fY;
- textShadow->color = blur.fColor;
- }
- return true;
- }
- return false;
- }
-
- static inline bool hasTextShadow(const SkPaint* paint) {
- return getTextShadow(paint, nullptr);
- }
-
- /**
* Build the best transform to use to rasterize text given a full
* transform matrix, and whether filteration is needed.
*
@@ -493,16 +442,6 @@ protected:
void drawTextureLayer(Layer* layer, const Rect& rect);
/**
- * Gets the alpha and xfermode out of a paint object. If the paint is null
- * alpha will be 255 and the xfermode will be SRC_OVER. Accounts for snapshot alpha.
- *
- * @param paint The paint to extract values from
- * @param alpha Where to store the resulting alpha
- * @param mode Where to store the resulting xfermode
- */
- inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const;
-
- /**
* Gets the alpha from a layer, accounting for snapshot alpha
*
* @param layer The layer from which the alpha is extracted
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index 4c4cd3da3be4..50199db75640 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -125,25 +125,32 @@ public:
}
bool intersects(float l, float t, float r, float b) const {
- return !intersectWith(l, t, r, b).isEmpty();
+ float tempLeft = std::max(left, l);
+ float tempTop = std::max(top, t);
+ float tempRight = std::min(right, r);
+ float tempBottom = std::min(bottom, b);
+
+ return ((tempLeft < tempRight) && (tempTop < tempBottom)); // !isEmpty
}
bool intersects(const Rect& r) const {
return intersects(r.left, r.top, r.right, r.bottom);
}
- bool intersect(float l, float t, float r, float b) {
- Rect tmp(l, t, r, b);
- intersectWith(tmp);
- if (!tmp.isEmpty()) {
- set(tmp);
- return true;
- }
- return false;
+ /**
+ * This method is named 'doIntersect' instead of 'intersect' so as not to be confused with
+ * SkRect::intersect / android.graphics.Rect#intersect behavior, which do not modify the object
+ * if the intersection of the rects would be empty.
+ */
+ void doIntersect(float l, float t, float r, float b) {
+ left = std::max(left, l);
+ top = std::max(top, t);
+ right = std::min(right, r);
+ bottom = std::min(bottom, b);
}
- bool intersect(const Rect& r) {
- return intersect(r.left, r.top, r.right, r.bottom);
+ void doIntersect(const Rect& r) {
+ doIntersect(r.left, r.top, r.right, r.bottom);
}
inline bool contains(float l, float t, float r, float b) const {
@@ -271,24 +278,6 @@ public:
void dump(const char* label = nullptr) const {
ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom);
}
-
-private:
- void intersectWith(Rect& tmp) const {
- tmp.left = std::max(left, tmp.left);
- tmp.top = std::max(top, tmp.top);
- tmp.right = std::min(right, tmp.right);
- tmp.bottom = std::min(bottom, tmp.bottom);
- }
-
- Rect intersectWith(float l, float t, float r, float b) const {
- Rect tmp;
- tmp.left = std::max(left, l);
- tmp.top = std::max(top, t);
- tmp.right = std::min(right, r);
- tmp.bottom = std::min(bottom, b);
- return tmp;
- }
-
}; // class Rect
}; // namespace uirenderer
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index ddc7ecd329b6..bf1b4d0b0d0e 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -725,7 +725,9 @@ template <class T>
void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) {
if (properties().getAlpha() <= 0.0f
|| properties().getOutline().getAlpha() <= 0.0f
- || !properties().getOutline().getPath()) {
+ || !properties().getOutline().getPath()
+ || properties().getScaleX() == 0
+ || properties().getScaleY() == 0) {
// no shadow to draw
return;
}
@@ -915,7 +917,10 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
const bool useViewProperties = (!mLayer || drawLayer);
if (useViewProperties) {
const Outline& outline = properties().getOutline();
- if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) {
+ if (properties().getAlpha() <= 0
+ || (outline.getShouldClip() && outline.isEmpty())
+ || properties().getScaleX() == 0
+ || properties().getScaleY() == 0) {
DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "",
this, getName());
return;
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index ad74bff8dc25..ce1bd6ab8b03 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -52,11 +52,8 @@ bool LayerProperties::setColorFilter(SkColorFilter* filter) {
bool LayerProperties::setFromPaint(const SkPaint* paint) {
bool changed = false;
- SkXfermode::Mode mode;
- int alpha;
- OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
- changed |= setAlpha(static_cast<uint8_t>(alpha));
- changed |= setXferMode(mode);
+ changed |= setAlpha(static_cast<uint8_t>(PaintUtils::getAlphaDirect(paint)));
+ changed |= setXferMode(PaintUtils::getXfermodeDirect(paint));
changed |= setColorFilter(paint ? paint->getColorFilter() : nullptr);
return changed;
}
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 71589c802749..f824cc020196 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -549,7 +549,7 @@ public:
if (flags & CLIP_TO_BOUNDS) {
outRect->set(0, 0, getWidth(), getHeight());
if (flags & CLIP_TO_CLIP_BOUNDS) {
- outRect->intersect(mPrimitiveFields.mClipBounds);
+ outRect->doIntersect(mPrimitiveFields.mClipBounds);
}
} else {
outRect->set(mPrimitiveFields.mClipBounds);
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 220936551a60..eb0fa74f5af0 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -73,8 +73,8 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
}
#if DEBUG_SHADOW
- ALOGD("light center %f %f %f",
- adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z);
+ ALOGD("light center %f %f %f %d",
+ adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z, lightRadius);
#endif
// light position (because it's in local space) needs to compensate for receiver transform
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 4d60b8dd0e7c..0a58f4b42e4c 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -44,7 +44,7 @@ Snapshot::Snapshot()
* Copies the specified snapshot/ The specified snapshot is stored as
* the previous snapshot.
*/
-Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags)
+Snapshot::Snapshot(Snapshot* s, int saveFlags)
: flags(0)
, previous(s)
, layer(s->layer)
@@ -148,7 +148,7 @@ void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const {
const Snapshot* current = this;
do {
snapshotList.push(current);
- current = current->previous.get();
+ current = current->previous;
} while (current);
// traverse the list, adding in each transform that contributes to the total transform
@@ -240,7 +240,7 @@ bool Snapshot::isIgnored() const {
void Snapshot::dump() const {
ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d",
- this, flags, previous.get(), getViewportHeight(), isIgnored(), !mClipArea->isSimple());
+ this, flags, previous, getViewportHeight(), isIgnored(), !mClipArea->isSimple());
const Rect& clipRect(mClipArea->getClipRect());
ALOGD(" ClipRect %.1f %.1f %.1f %.1f, clip simple %d",
clipRect.left, clipRect.top, clipRect.right, clipRect.bottom, mClipArea->isSimple());
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index cf8f11c80058..aeeda965c48f 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -83,11 +83,11 @@ public:
* Each snapshot has a link to a previous snapshot, indicating the previous
* state of the renderer.
*/
-class Snapshot: public LightRefBase<Snapshot> {
+class Snapshot {
public:
Snapshot();
- Snapshot(const sp<Snapshot>& s, int saveFlags);
+ Snapshot(Snapshot* s, int saveFlags);
/**
* Various flags set on ::flags.
@@ -229,7 +229,7 @@ public:
/**
* Previous snapshot.
*/
- sp<Snapshot> previous;
+ Snapshot* previous;
/**
* A pointer to the currently active layer.
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index 9b0a1aadf0bf..bdce73c79993 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -1051,7 +1051,7 @@ void SpotShadow::dumpPolygon(const Vector2* poly, int polyLength, const char* po
*/
void SpotShadow::dumpPolygon(const Vector3* poly, int polyLength, const char* polyName) {
for (int i = 0; i < polyLength; i++) {
- ALOGD("polygon %s i %d x %f y %f", polyName, i, poly[i].x, poly[i].y);
+ ALOGD("polygon %s i %d x %f y %f z %f", polyName, i, poly[i].x, poly[i].y, poly[i].z);
}
}
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index fb0753bbc76c..d680f990a0be 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -61,8 +61,6 @@ Font::FontDescription::FontDescription(const SkPaint* paint, const SkMatrix& ras
}
Font::~Font() {
- mState->removeFont(this);
-
for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
delete mCachedGlyphs.valueAt(i);
}
diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp
index c5126def683c..dfa70ace2f44 100644
--- a/libs/hwui/renderstate/RenderState.cpp
+++ b/libs/hwui/renderstate/RenderState.cpp
@@ -208,7 +208,7 @@ void RenderState::postDecStrong(VirtualLightRefBase* object) {
// Render
///////////////////////////////////////////////////////////////////////////////
-void RenderState::render(const Glop& glop) {
+void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) {
const Glop::Mesh& mesh = glop.mesh;
const Glop::Mesh::Vertices& vertices = mesh.vertices;
const Glop::Mesh::Indices& indices = mesh.indices;
@@ -223,7 +223,7 @@ void RenderState::render(const Glop& glop) {
fill.program->setColor(fill.color);
}
- fill.program->set(glop.transform.ortho,
+ fill.program->set(orthoMatrix,
glop.transform.modelView,
glop.transform.meshTransform(),
glop.transform.transformFlags & TransformFlags::OffsetByFudgeFactor);
diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h
index 4fd792c1b503..9ae084506f1d 100644
--- a/libs/hwui/renderstate/RenderState.h
+++ b/libs/hwui/renderstate/RenderState.h
@@ -84,7 +84,7 @@ public:
// more thinking...
void postDecStrong(VirtualLightRefBase* object);
- void render(const Glop& glop);
+ void render(const Glop& glop, const Matrix4& orthoMatrix);
AssetAtlas& assetAtlas() { return mAssetAtlas; }
Blend& blend() { return *mBlend; }
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9dc5b45a7738..38f6e539693e 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -62,7 +62,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
, mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
, mJankTracker(thread.timeLord().frameIntervalNanos())
, mProfiler(mFrames)
- , mContentOverdrawProtectionBounds(0, 0, 0, 0) {
+ , mContentDrawBounds(0, 0, 0, 0) {
mRenderNodes.emplace_back(rootRenderNode);
mRenderThread.renderState().registerCanvasContext(this);
mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
@@ -309,7 +309,7 @@ void CanvasContext::draw() {
Rect outBounds;
// It there are multiple render nodes, they are as follows:
// #0 - backdrop
- // #1 - content (with - and clipped to - bounds mContentOverdrawProtectionBounds)
+ // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
// #2 - frame
// Usually the backdrop cannot be seen since it will be entirely covered by the content. While
// resizing however it might become partially visible. The following render loop will crop the
@@ -317,66 +317,72 @@ void CanvasContext::draw() {
// against the backdrop (since that indicates a shrinking of the window) and then the frame
// around everything.
// The bounds of the backdrop against which the content should be clipped.
- Rect backdropBounds = mContentOverdrawProtectionBounds;
+ Rect backdropBounds = mContentDrawBounds;
+ // Usually the contents bounds should be mContentDrawBounds - however - we will
+ // move it towards the fixed edge to give it a more stable appearance (for the moment).
+ Rect contentBounds;
// If there is no content bounds we ignore the layering as stated above and start with 2.
- int layer = mContentOverdrawProtectionBounds.isEmpty() ? 2 : 0;
+ int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() <= 2) ? 2 : 0;
// Draw all render nodes. Note that
for (const sp<RenderNode>& node : mRenderNodes) {
if (layer == 0) { // Backdrop.
- // Draw the backdrop clipped to the inverse content bounds.
+ // Draw the backdrop clipped to the inverse content bounds, but assume that the content
+ // was moved to the upper left corner.
const RenderProperties& properties = node->properties();
Rect targetBounds(properties.getLeft(), properties.getTop(),
properties.getRight(), properties.getBottom());
+ // Move the content bounds towards the fixed corner of the backdrop.
+ const int x = targetBounds.left;
+ const int y = targetBounds.top;
+ contentBounds.set(x, y, x + mContentDrawBounds.getWidth(),
+ y + mContentDrawBounds.getHeight());
// Remember the intersection of the target bounds and the intersection bounds against
// which we have to crop the content.
- backdropBounds.intersect(targetBounds);
+ backdropBounds.set(x, y, x + backdropBounds.getWidth(), y + backdropBounds.getHeight());
+ backdropBounds.doIntersect(targetBounds);
// Check if we have to draw something on the left side ...
- if (targetBounds.left < mContentOverdrawProtectionBounds.left) {
+ if (targetBounds.left < contentBounds.left) {
mCanvas->save(SkCanvas::kClip_SaveFlag);
if (mCanvas->clipRect(targetBounds.left, targetBounds.top,
- mContentOverdrawProtectionBounds.left, targetBounds.bottom,
+ contentBounds.left, targetBounds.bottom,
SkRegion::kIntersect_Op)) {
mCanvas->drawRenderNode(node.get(), outBounds);
}
// Reduce the target area by the area we have just painted.
- targetBounds.left = std::min(mContentOverdrawProtectionBounds.left,
- targetBounds.right);
+ targetBounds.left = std::min(contentBounds.left, targetBounds.right);
mCanvas->restore();
}
// ... or on the right side ...
- if (targetBounds.right > mContentOverdrawProtectionBounds.right &&
+ if (targetBounds.right > contentBounds.right &&
!targetBounds.isEmpty()) {
mCanvas->save(SkCanvas::kClip_SaveFlag);
- if (mCanvas->clipRect(mContentOverdrawProtectionBounds.right, targetBounds.top,
+ if (mCanvas->clipRect(contentBounds.right, targetBounds.top,
targetBounds.right, targetBounds.bottom,
SkRegion::kIntersect_Op)) {
mCanvas->drawRenderNode(node.get(), outBounds);
}
// Reduce the target area by the area we have just painted.
- targetBounds.right = std::max(targetBounds.left,
- mContentOverdrawProtectionBounds.right);
+ targetBounds.right = std::max(targetBounds.left, contentBounds.right);
mCanvas->restore();
}
// ... or at the top ...
- if (targetBounds.top < mContentOverdrawProtectionBounds.top &&
+ if (targetBounds.top < contentBounds.top &&
!targetBounds.isEmpty()) {
mCanvas->save(SkCanvas::kClip_SaveFlag);
if (mCanvas->clipRect(targetBounds.left, targetBounds.top, targetBounds.right,
- mContentOverdrawProtectionBounds.top,
+ contentBounds.top,
SkRegion::kIntersect_Op)) {
mCanvas->drawRenderNode(node.get(), outBounds);
}
// Reduce the target area by the area we have just painted.
- targetBounds.top = std::min(mContentOverdrawProtectionBounds.top,
- targetBounds.bottom);
+ targetBounds.top = std::min(contentBounds.top, targetBounds.bottom);
mCanvas->restore();
}
// ... or at the bottom.
- if (targetBounds.bottom > mContentOverdrawProtectionBounds.bottom &&
+ if (targetBounds.bottom > contentBounds.bottom &&
!targetBounds.isEmpty()) {
mCanvas->save(SkCanvas::kClip_SaveFlag);
- if (mCanvas->clipRect(targetBounds.left,
- mContentOverdrawProtectionBounds.bottom, targetBounds.right,
+ if (mCanvas->clipRect(targetBounds.left, contentBounds.bottom, targetBounds.right,
targetBounds.bottom, SkRegion::kIntersect_Op)) {
mCanvas->drawRenderNode(node.get(), outBounds);
}
@@ -384,10 +390,17 @@ void CanvasContext::draw() {
}
} else if (layer == 1) { // Content
// It gets cropped against the bounds of the backdrop to stay inside.
- mCanvas->save(SkCanvas::kClip_SaveFlag);
- if (mCanvas->clipRect(backdropBounds.left, backdropBounds.top,
- backdropBounds.right, backdropBounds.bottom,
- SkRegion::kIntersect_Op)) {
+ mCanvas->save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
+
+ // We shift and clip the content to match its final location in the window.
+ const float left = mContentDrawBounds.left;
+ const float top = mContentDrawBounds.top;
+ const float dx = backdropBounds.left - left;
+ const float dy = backdropBounds.top - top;
+ const float width = backdropBounds.getWidth();
+ const float height = backdropBounds.getHeight();
+ mCanvas->translate(dx, dy);
+ if (mCanvas->clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op)) {
mCanvas->drawRenderNode(node.get(), outBounds);
}
mCanvas->restore();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 1c3845cac504..e0cbabdc933a 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -126,8 +126,8 @@ public:
mRenderNodes.end());
}
- void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
- mContentOverdrawProtectionBounds.set(left, top, right, bottom);
+ void setContentDrawBounds(int left, int top, int right, int bottom) {
+ mContentDrawBounds.set(left, top, right, bottom);
}
private:
@@ -167,7 +167,7 @@ private:
std::set<RenderNode*> mPrefetechedLayers;
// Stores the bounds of the main content.
- Rect mContentOverdrawProtectionBounds;
+ Rect mContentDrawBounds;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index f43a769890a4..26aae90d5990 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -529,15 +529,14 @@ void RenderProxy::drawRenderNode(RenderNode* node) {
staticPostAndWait(task);
}
-CREATE_BRIDGE5(setContentOverdrawProtectionBounds, CanvasContext* context, int left, int top,
+CREATE_BRIDGE5(setContentDrawBounds, CanvasContext* context, int left, int top,
int right, int bottom) {
- args->context->setContentOverdrawProtectionBounds(args->left, args->top, args->right,
- args->bottom);
+ args->context->setContentDrawBounds(args->left, args->top, args->right, args->bottom);
return nullptr;
}
-void RenderProxy::setContentOverdrawProtectionBounds(int left, int top, int right, int bottom) {
- SETUP_TASK(setContentOverdrawProtectionBounds);
+void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) {
+ SETUP_TASK(setContentDrawBounds);
args->context = mContext;
args->left = left;
args->top = top;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 046f24ac3f81..d1b62f1f64a6 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -109,7 +109,7 @@ public:
ANDROID_API void addRenderNode(RenderNode* node, bool placeFront);
ANDROID_API void removeRenderNode(RenderNode* node);
ANDROID_API void drawRenderNode(RenderNode* node);
- ANDROID_API void setContentOverdrawProtectionBounds(int left, int top, int right, int bottom);
+ ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom);
private:
RenderThread& mRenderThread;
diff --git a/libs/hwui/tests/Benchmark.h b/libs/hwui/tests/Benchmark.h
new file mode 100644
index 000000000000..e16310e034be
--- /dev/null
+++ b/libs/hwui/tests/Benchmark.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 TESTS_BENCHMARK_H
+#define TESTS_BENCHMARK_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace uirenderer {
+
+struct BenchmarkOptions {
+ int count;
+};
+
+typedef void (*BenchmarkFunctor)(const BenchmarkOptions&);
+
+struct BenchmarkInfo {
+ std::string name;
+ std::string description;
+ BenchmarkFunctor functor;
+};
+
+class Benchmark {
+public:
+ Benchmark(const BenchmarkInfo& info) {
+ registerBenchmark(info);
+ }
+
+private:
+ Benchmark() = delete;
+ Benchmark(const Benchmark&) = delete;
+ Benchmark& operator=(const Benchmark&) = delete;
+
+ static void registerBenchmark(const BenchmarkInfo& info);
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* TESTS_BENCHMARK_H */
diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp
index cebe7650dea2..ba763a8def62 100644
--- a/libs/hwui/tests/TestContext.cpp
+++ b/libs/hwui/tests/TestContext.cpp
@@ -22,16 +22,35 @@ namespace test {
static const int IDENT_DISPLAYEVENT = 1;
-static DisplayInfo getBuiltInDisplay() {
+static android::DisplayInfo DUMMY_DISPLAY {
+ 1080, //w
+ 1920, //h
+ 320.0, // xdpi
+ 320.0, // ydpi
+ 60.0, // fps
+ 2.0, // density
+ 0, // orientation
+ false, // secure?
+ 0, // appVsyncOffset
+ 0, // presentationDeadline
+ 0, // colorTransform
+};
+
+DisplayInfo getBuiltInDisplay() {
+#if !HWUI_NULL_GPU
DisplayInfo display;
sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &display);
LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n");
return display;
+#else
+ return DUMMY_DISPLAY;
+#endif
}
-android::DisplayInfo gDisplay = getBuiltInDisplay();
+// Initialize to a dummy default
+android::DisplayInfo gDisplay = DUMMY_DISPLAY;
TestContext::TestContext() {
mLooper = new Looper(true);
diff --git a/libs/hwui/tests/TestContext.h b/libs/hwui/tests/TestContext.h
index 7b30fc1dc7ce..2bbe5dffd9b8 100644
--- a/libs/hwui/tests/TestContext.h
+++ b/libs/hwui/tests/TestContext.h
@@ -32,6 +32,8 @@ namespace test {
extern DisplayInfo gDisplay;
#define dp(x) ((x) * android::uirenderer::test::gDisplay.density)
+DisplayInfo getBuiltInDisplay();
+
class TestContext {
public:
TestContext();
diff --git a/libs/hwui/tests/TreeContentAnimation.cpp b/libs/hwui/tests/TreeContentAnimation.cpp
new file mode 100644
index 000000000000..a59261c14fc5
--- /dev/null
+++ b/libs/hwui/tests/TreeContentAnimation.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <cutils/log.h>
+#include <gui/Surface.h>
+#include <ui/PixelFormat.h>
+
+#include <AnimationContext.h>
+#include <DisplayListCanvas.h>
+#include <RenderNode.h>
+#include <renderthread/RenderProxy.h>
+#include <renderthread/RenderTask.h>
+
+#include "Benchmark.h"
+#include "TestContext.h"
+
+#include "protos/hwui.pb.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <vector>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+using namespace android::uirenderer::test;
+
+class ContextFactory : public IContextFactory {
+public:
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
+ return new AnimationContext(clock);
+ }
+};
+
+static DisplayListCanvas* startRecording(RenderNode* node) {
+ DisplayListCanvas* renderer = new DisplayListCanvas(
+ node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
+ return renderer;
+}
+
+static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
+ node->setStagingDisplayList(renderer->finishRecording());
+ delete renderer;
+}
+
+class TreeContentAnimation {
+public:
+ virtual ~TreeContentAnimation() {}
+ int frameCount = 150;
+ virtual int getFrameCount() { return frameCount; }
+ virtual void setFrameCount(int fc) {
+ if (fc > 0) {
+ frameCount = fc;
+ }
+ }
+ virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
+ virtual void doFrame(int frameNr) = 0;
+
+ template <class T>
+ static void run(const BenchmarkOptions& opts) {
+ // Switch to the real display
+ gDisplay = getBuiltInDisplay();
+
+ T animation;
+ animation.setFrameCount(opts.count);
+
+ TestContext testContext;
+
+ // create the native surface
+ const int width = gDisplay.w;
+ const int height = gDisplay.h;
+ sp<Surface> surface = testContext.surface();
+
+ RenderNode* rootNode = new RenderNode();
+ rootNode->incStrong(nullptr);
+ rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
+ rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ rootNode->mutateStagingProperties().setClipToBounds(false);
+ rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+
+ ContextFactory factory;
+ std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
+ proxy->loadSystemProperties();
+ proxy->initialize(surface);
+ float lightX = width / 2.0;
+ proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
+ proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
+
+ android::uirenderer::Rect DUMMY;
+
+ DisplayListCanvas* renderer = startRecording(rootNode);
+ animation.createContent(width, height, renderer);
+ endRecording(renderer, rootNode);
+
+ // Do a few cold runs then reset the stats so that the caches are all hot
+ for (int i = 0; i < 3; i++) {
+ testContext.waitForVsync();
+ proxy->syncAndDrawFrame();
+ }
+ proxy->resetProfileInfo();
+
+ for (int i = 0; i < animation.getFrameCount(); i++) {
+ testContext.waitForVsync();
+
+ ATRACE_NAME("UI-Draw Frame");
+ nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
+ UiFrameInfoBuilder(proxy->frameInfo())
+ .setVsync(vsync, vsync);
+ animation.doFrame(i);
+ proxy->syncAndDrawFrame();
+ }
+
+ proxy->dumpProfileInfo(STDOUT_FILENO, 0);
+ rootNode->decStrong(nullptr);
+ }
+};
+
+class ShadowGridAnimation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _ShadowGrid(BenchmarkInfo{
+ "shadowgrid",
+ "A grid of rounded rects that cast a shadow. Simplified scenario of an "
+ "Android TV-style launcher interface. High CPU/GPU load.",
+ TreeContentAnimation::run<ShadowGridAnimation>
+});
+
+class ShadowGrid2Animation : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
+ for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
+ sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ for (size_t ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _ShadowGrid2(BenchmarkInfo{
+ "shadowgrid2",
+ "A dense grid of rounded rects that cast a shadow. This is a higher CPU load "
+ "variant of shadowgrid. Very high CPU load, high GPU load.",
+ TreeContentAnimation::run<ShadowGrid2Animation>
+});
+
+class RectGridAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ card = createCard(40, 40, 200, 200);
+ renderer->drawRenderNode(card.get());
+
+ renderer->insertReorderBarrier(false);
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
+
+ SkRegion region;
+ for (int xOffset = 0; xOffset < width; xOffset+=2) {
+ for (int yOffset = 0; yOffset < height; yOffset+=2) {
+ region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
+ }
+ }
+
+ SkPaint paint;
+ paint.setColor(0xff00ffff);
+ renderer->drawRegion(region, paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _RectGrid(BenchmarkInfo{
+ "rectgrid",
+ "A dense grid of 1x1 rects that should visually look like a single rect. "
+ "Low CPU/GPU load.",
+ TreeContentAnimation::run<RectGridAnimation>
+});
+
+class OvalAnimation : public TreeContentAnimation {
+public:
+ sp<RenderNode> card;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ card = createCard(40, 40, 400, 400);
+ renderer->drawRenderNode(card.get());
+
+ renderer->insertReorderBarrier(false);
+ }
+
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ card->mutateStagingProperties().setTranslationX(curFrame);
+ card->mutateStagingProperties().setTranslationY(curFrame);
+ card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(0xFF000000);
+ renderer->drawOval(0, 0, width, height, paint);
+
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _Oval(BenchmarkInfo{
+ "oval",
+ "Draws 1 oval.",
+ TreeContentAnimation::run<OvalAnimation>
+});
+
+class PartialDamageTest : public TreeContentAnimation {
+public:
+ std::vector< sp<RenderNode> > cards;
+ void createContent(int width, int height, DisplayListCanvas* renderer) override {
+ static SkColor COLORS[] = {
+ 0xFFF44336,
+ 0xFF9C27B0,
+ 0xFF2196F3,
+ 0xFF4CAF50,
+ };
+
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
+ COLORS[static_cast<int>((y / dp(116))) % 4]);
+ renderer->drawRenderNode(card.get());
+ cards.push_back(card);
+ }
+ }
+ }
+ void doFrame(int frameNr) override {
+ int curFrame = frameNr % 150;
+ cards[0]->mutateStagingProperties().setTranslationX(curFrame);
+ cards[0]->mutateStagingProperties().setTranslationY(curFrame);
+ cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(cards[0].get());
+ renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
+ SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, cards[0].get());
+ }
+
+ static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
+ int startA = (start >> 24) & 0xff;
+ int startR = (start >> 16) & 0xff;
+ int startG = (start >> 8) & 0xff;
+ int startB = start & 0xff;
+
+ int endA = (end >> 24) & 0xff;
+ int endR = (end >> 16) & 0xff;
+ int endG = (end >> 8) & 0xff;
+ int endB = end & 0xff;
+
+ return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
+ (int)((startR + (int)(fraction * (endR - startR))) << 16) |
+ (int)((startG + (int)(fraction * (endG - startG))) << 8) |
+ (int)((startB + (int)(fraction * (endB - startB))));
+ }
+private:
+ sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+
+ DisplayListCanvas* renderer = startRecording(node.get());
+ renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+ return node;
+ }
+};
+static Benchmark _PartialDamage(BenchmarkInfo{
+ "partialdamage",
+ "Tests the partial invalidation path. Draws a grid of rects and animates 1 "
+ "of them, should be low CPU & GPU load if EGL_EXT_buffer_age or "
+ "EGL_KHR_partial_update is supported by the device & are enabled in hwui.",
+ TreeContentAnimation::run<PartialDamageTest>
+});
diff --git a/libs/hwui/tests/how_to_run.txt b/libs/hwui/tests/how_to_run.txt
index 85900eff2f78..b051768f3262 100644
--- a/libs/hwui/tests/how_to_run.txt
+++ b/libs/hwui/tests/how_to_run.txt
@@ -2,16 +2,4 @@ mmm -j8 frameworks/base/libs/hwui/ &&
adb push $OUT/data/local/tmp/hwuitest /data/local/tmp/hwuitest &&
adb shell /data/local/tmp/hwuitest
-
-Command arguments:
-hwuitest [testname]
-
-Default test is 'shadowgrid'
-
-List of tests:
-
-shadowgrid: creates a grid of rounded rects that cast shadows, high CPU & GPU load
-
-rectgrid: creates a grid of 1x1 rects
-
-oval: draws 1 oval
+Pass --help to get help
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
index 0bbf08c76c90..aee84de3ae7b 100644
--- a/libs/hwui/tests/main.cpp
+++ b/libs/hwui/tests/main.cpp
@@ -14,385 +14,181 @@
* limitations under the License.
*/
-#include <cutils/log.h>
-#include <gui/Surface.h>
-#include <ui/PixelFormat.h>
-
-#include <AnimationContext.h>
-#include <DisplayListCanvas.h>
-#include <RenderNode.h>
-#include <renderthread/RenderProxy.h>
-#include <renderthread/RenderTask.h>
-
-#include "TestContext.h"
+#include "Benchmark.h"
#include "protos/hwui.pb.h"
+#include <getopt.h>
#include <stdio.h>
+#include <string>
#include <unistd.h>
+#include <unordered_map>
+#include <vector>
using namespace android;
using namespace android::uirenderer;
-using namespace android::uirenderer::renderthread;
-using namespace android::uirenderer::test;
-
-class ContextFactory : public IContextFactory {
-public:
- virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) override {
- return new AnimationContext(clock);
- }
-};
-static DisplayListCanvas* startRecording(RenderNode* node) {
- DisplayListCanvas* renderer = new DisplayListCanvas(
- node->stagingProperties().getWidth(), node->stagingProperties().getHeight());
- return renderer;
+// Not a static global because we need to force the map to be constructed
+// before we try to add things to it.
+std::unordered_map<std::string, BenchmarkInfo>& testMap() {
+ static std::unordered_map<std::string, BenchmarkInfo> testMap;
+ return testMap;
}
-static void endRecording(DisplayListCanvas* renderer, RenderNode* node) {
- node->setStagingDisplayList(renderer->finishRecording());
- delete renderer;
+void Benchmark::registerBenchmark(const BenchmarkInfo& info) {
+ testMap()[info.name] = info;
}
-class TreeContentAnimation {
-public:
- virtual ~TreeContentAnimation() {}
- int frameCount = 150;
- virtual int getFrameCount() { return frameCount; }
- virtual void setFrameCount(int fc) {
- if (fc > 0) {
- frameCount = fc;
- }
- }
- virtual void createContent(int width, int height, DisplayListCanvas* renderer) = 0;
- virtual void doFrame(int frameNr) = 0;
-
- template <class T>
- static void run(int frameCount) {
- T animation;
- animation.setFrameCount(frameCount);
-
- TestContext testContext;
-
- // create the native surface
- const int width = gDisplay.w;
- const int height = gDisplay.h;
- sp<Surface> surface = testContext.surface();
-
- RenderNode* rootNode = new RenderNode();
- rootNode->incStrong(nullptr);
- rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
- rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- rootNode->mutateStagingProperties().setClipToBounds(false);
- rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
-
- ContextFactory factory;
- std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode, &factory));
- proxy->loadSystemProperties();
- proxy->initialize(surface);
- float lightX = width / 2.0;
- proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15);
- proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
-
- android::uirenderer::Rect DUMMY;
-
- DisplayListCanvas* renderer = startRecording(rootNode);
- animation.createContent(width, height, renderer);
- endRecording(renderer, rootNode);
-
- // Do a few cold runs then reset the stats so that the caches are all hot
- for (int i = 0; i < 3; i++) {
- testContext.waitForVsync();
- proxy->syncAndDrawFrame();
- }
- proxy->resetProfileInfo();
-
- for (int i = 0; i < animation.getFrameCount(); i++) {
- testContext.waitForVsync();
-
- ATRACE_NAME("UI-Draw Frame");
- nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
- UiFrameInfoBuilder(proxy->frameInfo())
- .setVsync(vsync, vsync);
- animation.doFrame(i);
- proxy->syncAndDrawFrame();
- }
-
- proxy->dumpProfileInfo(STDOUT_FILENO, 0);
- rootNode->decStrong(nullptr);
- }
-};
-
-class ShadowGridAnimation : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
+static int gFrameCount = 150;
+static int gRepeatCount = 1;
+static std::vector<BenchmarkInfo> gRunTests;
+
+static void printHelp() {
+ printf("\
+USAGE: hwuitest [OPTIONS] <TESTNAME>\n\
+\n\
+OPTIONS:\n\
+ -c, --count=NUM NUM loops a test should run (example, number of frames)\n\
+ -r, --runs=NUM Repeat the test(s) NUM times\n\
+ -h, --help Display this help\n\
+ --list List all tests\n\
+\n");
+}
- for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
- for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
- sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
+static void listTests() {
+ printf("Tests: \n");
+ for (auto&& test : testMap()) {
+ auto&& info = test.second;
+ const char* col1 = info.name.c_str();
+ int dlen = info.description.length();
+ const char* col2 = info.description.c_str();
+ // World's best line breaking algorithm.
+ do {
+ int toPrint = dlen;
+ if (toPrint > 50) {
+ char* found = (char*) memrchr(col2, ' ', 50);
+ if (found) {
+ toPrint = found - col2;
+ } else {
+ toPrint = 50;
+ }
}
- }
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class ShadowGrid2Animation : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- for (int x = dp(8); x < (width - dp(58)); x += dp(58)) {
- for (int y = dp(8); y < (height - dp(58)); y += dp(58)) {
- sp<RenderNode> card = createCard(x, y, dp(50), dp(50));
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
+ printf("%-20s %.*s\n", col1, toPrint, col2);
+ col1 = "";
+ col2 += toPrint;
+ dlen -= toPrint;
+ while (*col2 == ' ') {
+ col2++; dlen--;
}
- }
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->mutateStagingProperties().setElevation(dp(16));
- node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(6), 1);
- node->mutateStagingProperties().mutableOutline().setShouldClip(true);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-class RectGridAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- card = createCard(40, 40, 200, 200);
- renderer->drawRenderNode(card.get());
-
- renderer->insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ } while (dlen > 0);
+ printf("\n");
}
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(0xFFFF00FF, SkXfermode::kSrcOver_Mode);
-
- SkRegion region;
- for (int xOffset = 0; xOffset < width; xOffset+=2) {
- for (int yOffset = 0; yOffset < height; yOffset+=2) {
- region.op(xOffset, yOffset, xOffset + 1, yOffset + 1, SkRegion::kUnion_Op);
- }
- }
-
- SkPaint paint;
- paint.setColor(0xff00ffff);
- renderer->drawRegion(region, paint);
+}
- endRecording(renderer, node.get());
- return node;
- }
+static const struct option LONG_OPTIONS[] = {
+ { "frames", required_argument, nullptr, 'f' },
+ { "repeat", required_argument, nullptr, 'r' },
+ { "help", no_argument, nullptr, 'h' },
+ { "list", no_argument, nullptr, 'l' },
+ { 0, 0, 0, 0 }
};
-class OvalAnimation : public TreeContentAnimation {
-public:
- sp<RenderNode> card;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- renderer->insertReorderBarrier(true);
-
- card = createCard(40, 40, 400, 400);
- renderer->drawRenderNode(card.get());
+static const char* SHORT_OPTIONS = "c:r:h";
- renderer->insertReorderBarrier(false);
- }
+void parseOptions(int argc, char* argv[]) {
+ int c;
+ // temporary variable
+ int count;
+ bool error = false;
+ opterr = 0;
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- card->mutateStagingProperties().setTranslationX(curFrame);
- card->mutateStagingProperties().setTranslationY(curFrame);
- card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ while (true) {
- DisplayListCanvas* renderer = startRecording(node.get());
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setColor(0xFF000000);
- renderer->drawOval(0, 0, width, height, paint);
+ c = getopt_long(argc, argv, SHORT_OPTIONS, LONG_OPTIONS, &option_index);
- endRecording(renderer, node.get());
- return node;
- }
-};
+ if (c == -1)
+ break;
-class PartialInvalTest : public TreeContentAnimation {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, DisplayListCanvas* renderer) override {
- static SkColor COLORS[] = {
- 0xFFF44336,
- 0xFF9C27B0,
- 0xFF2196F3,
- 0xFF4CAF50,
- };
+ switch (c) {
+ case 0:
+ // Option set a flag, don't need to do anything
+ // (although none of the current LONG_OPTIONS do this...)
+ break;
- renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ case 'l':
+ listTests();
+ exit(EXIT_SUCCESS);
+ break;
- for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
- for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
- sp<RenderNode> card = createCard(x, y, dp(100), dp(100),
- COLORS[static_cast<int>((y / dp(116))) % 4]);
- renderer->drawRenderNode(card.get());
- cards.push_back(card);
+ case 'c':
+ count = atoi(optarg);
+ if (!count) {
+ fprintf(stderr, "Invalid frames argument '%s'\n", optarg);
+ error = true;
+ } else {
+ gFrameCount = (count > 0 ? count : INT_MAX);
+ }
+ break;
+
+ case 'r':
+ count = atoi(optarg);
+ if (!count) {
+ fprintf(stderr, "Invalid repeat argument '%s'\n", optarg);
+ error = true;
+ } else {
+ gRepeatCount = (count > 0 ? count : INT_MAX);
}
+ break;
+
+ case 'h':
+ printHelp();
+ exit(EXIT_SUCCESS);
+ break;
+
+ case '?':
+ fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]);
+ // fall-through
+ default:
+ error = true;
+ break;
}
}
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 150;
- cards[0]->mutateStagingProperties().setTranslationX(curFrame);
- cards[0]->mutateStagingProperties().setTranslationY(curFrame);
- cards[0]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- DisplayListCanvas* renderer = startRecording(cards[0].get());
- renderer->drawColor(interpolateColor(curFrame / 150.0f, 0xFFF44336, 0xFFF8BBD0),
- SkXfermode::kSrcOver_Mode);
- endRecording(renderer, cards[0].get());
+ if (error) {
+ fprintf(stderr, "Try 'hwuitest --help' for more information.\n");
+ exit(EXIT_FAILURE);
}
- static SkColor interpolateColor(float fraction, SkColor start, SkColor end) {
- int startA = (start >> 24) & 0xff;
- int startR = (start >> 16) & 0xff;
- int startG = (start >> 8) & 0xff;
- int startB = start & 0xff;
-
- int endA = (end >> 24) & 0xff;
- int endR = (end >> 16) & 0xff;
- int endG = (end >> 8) & 0xff;
- int endB = end & 0xff;
-
- return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
- (int)((startR + (int)(fraction * (endR - startR))) << 16) |
- (int)((startG + (int)(fraction * (endG - startG))) << 8) |
- (int)((startB + (int)(fraction * (endB - startB))));
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height, SkColor color) {
- sp<RenderNode> node = new RenderNode();
- node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
- node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
-
- DisplayListCanvas* renderer = startRecording(node.get());
- renderer->drawColor(color, SkXfermode::kSrcOver_Mode);
- endRecording(renderer, node.get());
- return node;
- }
-};
-
-struct cstr_cmp {
- bool operator()(const char *a, const char *b) const {
- return std::strcmp(a, b) < 0;
+ /* Print any remaining command line arguments (not options). */
+ if (optind < argc) {
+ do {
+ const char* test = argv[optind++];
+ auto pos = testMap().find(test);
+ if (pos == testMap().end()) {
+ fprintf(stderr, "Unknown test '%s'\n", test);
+ exit(EXIT_FAILURE);
+ } else {
+ gRunTests.push_back(pos->second);
+ }
+ } while (optind < argc);
+ } else {
+ gRunTests.push_back(testMap()["shadowgrid"]);
}
-};
-
-typedef void (*testProc)(int);
-
-std::map<const char*, testProc, cstr_cmp> gTestMap {
- {"shadowgrid", TreeContentAnimation::run<ShadowGridAnimation>},
- {"shadowgrid2", TreeContentAnimation::run<ShadowGrid2Animation>},
- {"rectgrid", TreeContentAnimation::run<RectGridAnimation> },
- {"oval", TreeContentAnimation::run<OvalAnimation> },
- {"partialinval", TreeContentAnimation::run<PartialInvalTest> },
-};
+}
int main(int argc, char* argv[]) {
- const char* testName = argc > 1 ? argv[1] : "shadowgrid";
- testProc proc = gTestMap[testName];
- if(!proc) {
- printf("Error: couldn't find test %s\n", testName);
- return 1;
- }
- int loopCount = 1;
- if (argc > 2) {
- loopCount = atoi(argv[2]);
- if (!loopCount) {
- printf("Invalid loop count!\n");
- return 1;
- }
- }
- int frameCount = 150;
- if (argc > 3) {
- frameCount = atoi(argv[3]);
- if (frameCount < 1) {
- printf("Invalid frame count!\n");
- return 1;
+ parseOptions(argc, argv);
+
+ BenchmarkOptions opts;
+ opts.count = gFrameCount;
+ for (int i = 0; i < gRepeatCount; i++) {
+ for (auto&& test : gRunTests) {
+ test.functor(opts);
}
}
- if (loopCount < 0) {
- loopCount = INT_MAX;
- }
- for (int i = 0; i < loopCount; i++) {
- proc(frameCount);
- }
printf("Success!\n");
return 0;
}
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index ba02f5f1a77d..d00236ed955a 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -16,12 +16,19 @@
#ifndef PAINT_UTILS_H
#define PAINT_UTILS_H
+#include <utils/Blur.h>
+
#include <SkColorFilter.h>
+#include <SkDrawLooper.h>
#include <SkXfermode.h>
namespace android {
namespace uirenderer {
+/**
+ * Utility methods for accessing data within SkPaint, and providing defaults
+ * with optional SkPaint pointers.
+ */
class PaintUtils {
public:
@@ -73,6 +80,39 @@ public:
return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
}
+ struct TextShadow {
+ SkScalar radius;
+ float dx;
+ float dy;
+ SkColor color;
+ };
+
+ static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) {
+ SkDrawLooper::BlurShadowRec blur;
+ if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) {
+ if (textShadow) {
+ textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma);
+ textShadow->dx = blur.fOffset.fX;
+ textShadow->dy = blur.fOffset.fY;
+ textShadow->color = blur.fColor;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ static inline bool hasTextShadow(const SkPaint* paint) {
+ return getTextShadow(paint, nullptr);
+ }
+
+ static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) {
+ return paint ? getXfermode(paint->getXfermode()) : SkXfermode::kSrcOver_Mode;
+ }
+
+ static inline int getAlphaDirect(const SkPaint* paint) {
+ return paint ? paint->getAlpha() : 255;
+ }
+
}; // class PaintUtils
} /* namespace uirenderer */
diff --git a/libs/hwui/utils/TinyHashMap.h b/libs/hwui/utils/TinyHashMap.h
deleted file mode 100644
index 4ff9a42f6d5d..000000000000
--- a/libs/hwui/utils/TinyHashMap.h
+++ /dev/null
@@ -1,70 +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.
- */
-
-#ifndef ANDROID_HWUI_TINYHASHMAP_H
-#define ANDROID_HWUI_TINYHASHMAP_H
-
-#include <utils/BasicHashtable.h>
-
-namespace android {
-namespace uirenderer {
-
-/**
- * A very simple hash map that doesn't allow duplicate keys, overwriting the older entry.
- */
-template <typename TKey, typename TValue>
-class TinyHashMap {
-public:
- typedef key_value_pair_t<TKey, TValue> TEntry;
-
- /**
- * Puts an entry in the hash, removing any existing entry with the same key
- */
- void put(TKey key, TValue value) {
- hash_t hash = android::hash_type(key);
-
- ssize_t index = mTable.find(-1, hash, key);
- if (index != -1) {
- mTable.removeAt(index);
- }
-
- TEntry initEntry(key, value);
- mTable.add(hash, initEntry);
- }
-
- /**
- * Return true if key is in the map, in which case stores the value in the output ref
- */
- bool get(TKey key, TValue& outValue) {
- hash_t hash = android::hash_type(key);
- ssize_t index = mTable.find(-1, hash, key);
- if (index == -1) {
- return false;
- }
- outValue = mTable.entryAt(index).value;
- return true;
- }
-
- void clear() { mTable.clear(); }
-
-private:
- BasicHashtable<TKey, TEntry> mTable;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // ANDROID_HWUI_TINYHASHMAP_H
diff --git a/media/java/android/media/MediaActionSound.java b/media/java/android/media/MediaActionSound.java
index 2f4d136999f6..1fee58707613 100644
--- a/media/java/android/media/MediaActionSound.java
+++ b/media/java/android/media/MediaActionSound.java
@@ -52,7 +52,7 @@ public class MediaActionSound {
"/system/media/audio/ui/camera_click.ogg",
"/system/media/audio/ui/camera_focus.ogg",
"/system/media/audio/ui/VideoRecord.ogg",
- "/system/media/audio/ui/VideoRecord.ogg"
+ "/system/media/audio/ui/VideoStop.ogg"
};
private static final String TAG = "MediaActionSound";
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index bc1aedad1048..1c2c9406621d 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -479,11 +479,6 @@ import java.lang.ref.WeakReference;
* <td>{} </p></td>
* <td>This method can be called in any state and calling it does not change
* the object state. </p></td></tr>
- * <tr><td>setPlaybackRate</p></td>
- * <td>any </p></td>
- * <td>{} </p></td>
- * <td>This method can be called in any state and calling it does not change
- * the object state. </p></td></tr>
* <tr><td>setPlaybackParams</p></td>
* <td>any </p></td>
* <td>{} </p></td>
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index ed2c4cbd248d..8ac86b01dcb8 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -807,6 +807,32 @@ public class MediaRecorder
public native void stop() throws IllegalStateException;
/**
+ * Pauses recording. Call this after start(). You may resume recording
+ * with resume() without reconfiguration, as opposed to stop(). It does
+ * nothing if the recording is already paused.
+ *
+ * When the recording is paused and resumed, the resulting output would
+ * be as if nothing happend during paused period, immediately switching
+ * to the resumed scene.
+ *
+ * @throws IllegalStateException if it is called before start() or after
+ * stop()
+ * {@hide}
+ */
+ public native void pause() throws IllegalStateException;
+
+ /**
+ * Resumes recording. Call this after start(). It does nothing if the
+ * recording is not paused.
+ *
+ * @throws IllegalStateException if it is called before start() or after
+ * stop()
+ * @see android.media.MediaRecorder#pause
+ * {@hide}
+ */
+ public native void resume() throws IllegalStateException;
+
+ /**
* Restarts the MediaRecorder to its idle state. After calling
* this method, you will have to configure it again as if it had just been
* constructed.
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
index 54ad60e4a62d..4b8f81e01a1d 100644
--- a/media/java/android/media/Metadata.java
+++ b/media/java/android/media/Metadata.java
@@ -18,6 +18,7 @@ package android.media;
import android.os.Parcel;
import android.util.Log;
+import android.util.MathUtils;
import java.util.Calendar;
import java.util.Collections;
@@ -332,7 +333,14 @@ import java.util.TimeZone;
}
// Skip to the next one.
- parcel.setDataPosition(start + size);
+ try {
+ parcel.setDataPosition(MathUtils.addOrThrow(start, size));
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Invalid size: " + e.getMessage());
+ error = true;
+ break;
+ }
+
bytesLeft -= size;
++recCount;
}
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index e05b3483aadc..701f7ac8d5bf 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -399,6 +399,22 @@ android_media_MediaRecorder_stop(JNIEnv *env, jobject thiz)
}
static void
+android_media_MediaRecorder_pause(JNIEnv *env, jobject thiz)
+{
+ ALOGV("pause");
+ sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+ process_media_recorder_call(env, mr->pause(), "java/lang/RuntimeException", "pause failed.");
+}
+
+static void
+android_media_MediaRecorder_resume(JNIEnv *env, jobject thiz)
+{
+ ALOGV("resume");
+ sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
+ process_media_recorder_call(env, mr->resume(), "java/lang/RuntimeException", "resume failed.");
+}
+
+static void
android_media_MediaRecorder_native_reset(JNIEnv *env, jobject thiz)
{
ALOGV("native_reset");
@@ -528,6 +544,8 @@ static const JNINativeMethod gMethods[] = {
{"getMaxAmplitude", "()I", (void *)android_media_MediaRecorder_native_getMaxAmplitude},
{"start", "()V", (void *)android_media_MediaRecorder_start},
{"stop", "()V", (void *)android_media_MediaRecorder_stop},
+ {"pause", "()V", (void *)android_media_MediaRecorder_pause},
+ {"resume", "()V", (void *)android_media_MediaRecorder_resume},
{"native_reset", "()V", (void *)android_media_MediaRecorder_native_reset},
{"release", "()V", (void *)android_media_MediaRecorder_release},
{"native_init", "()V", (void *)android_media_MediaRecorder_native_init},
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 295cb80ade52..f9e80275b10d 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -3,7 +3,7 @@
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.REMOVE_TASKS" />
- <uses-permission android:name="android.permission.REORDER_TASKS" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:name=".DocumentsApplication"
diff --git a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
index 6dcbb38cb950..ab414a9bb6af 100644
--- a/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
+++ b/packages/DocumentsUI/res/color/item_doc_grid_overlay.xml
@@ -16,6 +16,10 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
+ android:state_focused="true"
+ android:color="@color/platform_blue_a200"
+ android:alpha="0.1" />
+ <item
android:state_activated="true"
android:color="?android:attr/colorAccent"
android:alpha="0.1" />
@@ -25,4 +29,4 @@
android:alpha="0.5" />
<item
android:color="@android:color/transparent" />
-</selector> \ No newline at end of file
+</selector>
diff --git a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
index 7d7a110864d9..90e2b7e653c6 100644
--- a/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
+++ b/packages/DocumentsUI/res/color/item_doc_list_background_activated.xml
@@ -16,6 +16,10 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
+ android:state_focused="true"
+ android:color="@color/platform_blue_a200"
+ android:alpha="0.1" />
+ <item
android:state_activated="true"
android:color="?android:attr/colorAccent"
android:alpha="0.1" />
diff --git a/packages/DocumentsUI/res/menu/activity.xml b/packages/DocumentsUI/res/menu/activity.xml
index 673a254f63f5..7e0649be722d 100644
--- a/packages/DocumentsUI/res/menu/activity.xml
+++ b/packages/DocumentsUI/res/menu/activity.xml
@@ -23,13 +23,6 @@
android:actionViewClass="android.widget.SearchView"
android:imeOptions="actionSearch" />
<item
- android:id="@+id/menu_create_dir"
- android:title="@string/menu_create_dir"
- android:icon="@drawable/ic_menu_new_folder"
- android:alphabeticShortcut="e"
- android:showAsAction="always"
- android:visible="false" />
- <item
android:id="@+id/menu_sort"
android:title="@string/menu_sort"
android:icon="@drawable/ic_menu_sortby"
@@ -63,6 +56,13 @@
android:showAsAction="never"
android:visible="false" />
<item
+ android:id="@+id/menu_create_dir"
+ android:title="@string/menu_create_dir"
+ android:icon="@drawable/ic_menu_new_folder"
+ android:alphabeticShortcut="e"
+ android:showAsAction="always"
+ android:visible="false" />
+ <item
android:id="@+id/menu_paste_from_clipboard"
android:title="@string/menu_paste_from_clipboard"
android:alphabeticShortcut="v"
@@ -70,11 +70,11 @@
android:visible="false" />
<!-- Copy action is defined in mode_directory.xml -->
<item
- android:id="@+id/menu_advanced"
+ android:id="@+id/menu_file_size"
android:showAsAction="never"
android:visible="false" />
<item
- android:id="@+id/menu_file_size"
+ android:id="@+id/menu_advanced"
android:showAsAction="never"
android:visible="false" />
<item
diff --git a/packages/DocumentsUI/res/values-af/strings.xml b/packages/DocumentsUI/res/values-af/strings.xml
index c2b6dbcaa791..3daecc758843 100644
--- a/packages/DocumentsUI/res/values-af/strings.xml
+++ b/packages/DocumentsUI/res/values-af/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Lêers"</string>
<string name="title_open" msgid="4353228937663917801">"Maak oop vanuit"</string>
<string name="title_save" msgid="2433679664882857999">"Stoor na"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Skep vouer"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nuwe vouer"</string>
<string name="menu_grid" msgid="6878021334497835259">"Roosteraansig"</string>
<string name="menu_list" msgid="7279285939892417279">"Lysaansig"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sorteer volgens"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Kies alles"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopieer na …"</string>
<string name="menu_move" msgid="1828090633118079817">"Skuif na …"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nuwe venster"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopieer"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Plak"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Wys interne berging"</string>
diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml
index b82fa937fbac..43db786c582f 100644
--- a/packages/DocumentsUI/res/values-am/strings.xml
+++ b/packages/DocumentsUI/res/values-am/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ፋይሎች"</string>
<string name="title_open" msgid="4353228937663917801">"ክፈት ከ"</string>
<string name="title_save" msgid="2433679664882857999">"አስቀምጥ ወደ"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"አቃፊ ፍጠር"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"አዲስ አቃፊ"</string>
<string name="menu_grid" msgid="6878021334497835259">"የፍርግርግ እይታ"</string>
<string name="menu_list" msgid="7279285939892417279">"የዝርዝር እይታ"</string>
<string name="menu_sort" msgid="7677740407158414452">"ደርድር በ"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"ሁሉንም ምረጥ"</string>
<string name="menu_copy" msgid="3612326052677229148">"ቅዳ ወደ…"</string>
<string name="menu_move" msgid="1828090633118079817">"ይውሰዱ ወደ..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"አዲሰ መስኮት"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"ቅዳ"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ለጥፍ"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ውስጣዊ ማከማቻ አሳይ"</string>
diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml
index c7c9ef2405a7..88c42d03d1d6 100644
--- a/packages/DocumentsUI/res/values-ar/strings.xml
+++ b/packages/DocumentsUI/res/values-ar/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"الملفات"</string>
<string name="title_open" msgid="4353228937663917801">"فتح من"</string>
<string name="title_save" msgid="2433679664882857999">"حفظ في"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"إنشاء مجلد"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"مجلد جديد"</string>
<string name="menu_grid" msgid="6878021334497835259">"عرض الشبكة"</string>
<string name="menu_list" msgid="7279285939892417279">"عرض القائمة"</string>
<string name="menu_sort" msgid="7677740407158414452">"ترتيب بحسب"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"تحديد الكل"</string>
<string name="menu_copy" msgid="3612326052677229148">"نسخ إلى…"</string>
<string name="menu_move" msgid="1828090633118079817">"نقل إلى..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"نافذة جديدة"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"نسخ"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"لصق"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"إظهار وحدة التخزين الداخلية"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"نسخ"</string>
<string name="button_move" msgid="2202666023104202232">"نقل"</string>
<string name="button_dismiss" msgid="3714065566893946085">"إزالة"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"إعادة المحاولة"</string>
<string name="sort_name" msgid="9183560467917256779">"بحسب الاسم"</string>
<string name="sort_date" msgid="586080032956151448">"بحسب تاريخ التعديل"</string>
<string name="sort_size" msgid="3350681319735474741">"بحسب الحجم"</string>
diff --git a/packages/DocumentsUI/res/values-az-rAZ/strings.xml b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
index 8c79cebca33f..f91f2ee5a782 100644
--- a/packages/DocumentsUI/res/values-az-rAZ/strings.xml
+++ b/packages/DocumentsUI/res/values-az-rAZ/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Fayllar"</string>
<string name="title_open" msgid="4353228937663917801">"Vasitəsilə açın"</string>
<string name="title_save" msgid="2433679664882857999">"buraya saxlayın"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Qovluq yaradın"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Yeni qovluq"</string>
<string name="menu_grid" msgid="6878021334497835259">"Torlu görünüş"</string>
<string name="menu_list" msgid="7279285939892417279">"Siyahı görünüşü"</string>
<string name="menu_sort" msgid="7677740407158414452">"Bunlardan biri üzrə sırala"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Hamısını seçin"</string>
<string name="menu_copy" msgid="3612326052677229148">"Buraya kopyalayın:"</string>
<string name="menu_move" msgid="1828090633118079817">"Köçürün…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Yeni pəncərə"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopyalayın"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Yerləşdirin"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Daxili yaddaşı göstərin"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopyala"</string>
<string name="button_move" msgid="2202666023104202232">"Köçürün"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Rədd edin"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Yenidən cəhd edin"</string>
<string name="sort_name" msgid="9183560467917256779">"Ad üzrə"</string>
<string name="sort_date" msgid="586080032956151448">"Tarix üzrə dəyişmiş"</string>
<string name="sort_size" msgid="3350681319735474741">"Ölçü üzrə"</string>
diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml
index fdf57d091926..67a1c6a759c5 100644
--- a/packages/DocumentsUI/res/values-bg/strings.xml
+++ b/packages/DocumentsUI/res/values-bg/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Файлове"</string>
<string name="title_open" msgid="4353228937663917801">"Отваряне от"</string>
<string name="title_save" msgid="2433679664882857999">"Запазване във:"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Създаване на папка"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Нова папка"</string>
<string name="menu_grid" msgid="6878021334497835259">"Изглед в мрежа"</string>
<string name="menu_list" msgid="7279285939892417279">"Списъчен изглед"</string>
<string name="menu_sort" msgid="7677740407158414452">"Сортиране по"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Избиране на всичко"</string>
<string name="menu_copy" msgid="3612326052677229148">"Копиране във…"</string>
<string name="menu_move" msgid="1828090633118079817">"Преместване във…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Нов прозорец"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Копиране"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Поставяне"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Вътр. хранилище: Показв."</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Копиране"</string>
<string name="button_move" msgid="2202666023104202232">"Преместване"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Отхвърляне"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Нов опит"</string>
<string name="sort_name" msgid="9183560467917256779">"По име"</string>
<string name="sort_date" msgid="586080032956151448">"По дата на промяната"</string>
<string name="sort_size" msgid="3350681319735474741">"По размер"</string>
diff --git a/packages/DocumentsUI/res/values-bn-rBD/strings.xml b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
index 7caf57fbbed7..b01cc84e5ce5 100644
--- a/packages/DocumentsUI/res/values-bn-rBD/strings.xml
+++ b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ফাইলগুলি"</string>
<string name="title_open" msgid="4353228937663917801">"এখান থেকে খুলুন"</string>
<string name="title_save" msgid="2433679664882857999">"এতে সংরক্ষণ করুন"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"ফোল্ডার তৈরি করুন"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"নতুন ফোল্ডার"</string>
<string name="menu_grid" msgid="6878021334497835259">"গ্রিড দৃশ্য"</string>
<string name="menu_list" msgid="7279285939892417279">"তালিকা দৃশ্য"</string>
<string name="menu_sort" msgid="7677740407158414452">"এই অনুসারে বাছুন"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"সবগুলি নির্বাচন করুন"</string>
<string name="menu_copy" msgid="3612326052677229148">"এতে অনুলিপি করুন…"</string>
<string name="menu_move" msgid="1828090633118079817">"এতে সরান..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"নতুন উইন্ডো"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"প্রতিলিপি করুন"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"আটকান"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"অভ্যন্তরীণ সঞ্চয়স্থান দেখান"</string>
diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml
index ab365ce82801..663608ba832f 100644
--- a/packages/DocumentsUI/res/values-ca/strings.xml
+++ b/packages/DocumentsUI/res/values-ca/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Fitxers"</string>
<string name="title_open" msgid="4353228937663917801">"Obre des de"</string>
<string name="title_save" msgid="2433679664882857999">"Desa a"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Crea una carpeta"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Carpeta nova"</string>
<string name="menu_grid" msgid="6878021334497835259">"Visualització de quadrícula"</string>
<string name="menu_list" msgid="7279285939892417279">"Visualització de llista"</string>
<string name="menu_sort" msgid="7677740407158414452">"Ordena per"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Selecciona-ho tot"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copia a…"</string>
<string name="menu_move" msgid="1828090633118079817">"Mou a..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Finestra nova"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copia"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Enganxa"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostra emmagatz. intern"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Copia"</string>
<string name="button_move" msgid="2202666023104202232">"Desplaça"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Ignora"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Torna-ho a provar"</string>
<string name="sort_name" msgid="9183560467917256779">"Per nom"</string>
<string name="sort_date" msgid="586080032956151448">"Per data de modificació"</string>
<string name="sort_size" msgid="3350681319735474741">"Per mida"</string>
diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml
index 62b313c94bdc..81d8dc8caa48 100644
--- a/packages/DocumentsUI/res/values-cs/strings.xml
+++ b/packages/DocumentsUI/res/values-cs/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Soubory"</string>
<string name="title_open" msgid="4353228937663917801">"Otevřít"</string>
<string name="title_save" msgid="2433679664882857999">"Uložit do"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Vytvořit složku"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nová složka"</string>
<string name="menu_grid" msgid="6878021334497835259">"Mřížkové zobrazení"</string>
<string name="menu_list" msgid="7279285939892417279">"Zobrazení seznamu"</string>
<string name="menu_sort" msgid="7677740407158414452">"Řadit podle"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Vybrat vše"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopírovat do…"</string>
<string name="menu_move" msgid="1828090633118079817">"Přesunout do…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nové okno"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopírovat"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Vložit"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Zobrazit inter. úložiště"</string>
diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml
index c3d7cdd39d08..e10904847a9f 100644
--- a/packages/DocumentsUI/res/values-da/strings.xml
+++ b/packages/DocumentsUI/res/values-da/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Filer"</string>
<string name="title_open" msgid="4353228937663917801">"Åbn fra"</string>
<string name="title_save" msgid="2433679664882857999">"Gem i"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Opret mappe"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Ny mappe"</string>
<string name="menu_grid" msgid="6878021334497835259">"Gittervisning"</string>
<string name="menu_list" msgid="7279285939892417279">"Listevisning"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sortér efter"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Markér alle"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopiér til…"</string>
<string name="menu_move" msgid="1828090633118079817">"Flyt til…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nyt vindue"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiér"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Indsæt"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Vis intern lagerplads"</string>
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index f88b5c4cb92e..7c467ef10d39 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Dateien"</string>
<string name="title_open" msgid="4353228937663917801">"Öffnen von"</string>
<string name="title_save" msgid="2433679664882857999">"Speichern unter"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Ordner erstellen"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Neuer Ordner"</string>
<string name="menu_grid" msgid="6878021334497835259">"Rasteransicht"</string>
<string name="menu_list" msgid="7279285939892417279">"Listenansicht"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sortieren nach"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Alle auswählen"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopieren nach..."</string>
<string name="menu_move" msgid="1828090633118079817">"Verschieben nach…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Neues Fenster"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopieren"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Einfügen"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Int. Speicher anzeigen"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index dcca46c776b2..a350002074a6 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Αρχεία"</string>
<string name="title_open" msgid="4353228937663917801">"Άνοιγμα από"</string>
<string name="title_save" msgid="2433679664882857999">"Αποθήκευση σε"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Δημιουργία φακέλου"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Νέος φάκελος"</string>
<string name="menu_grid" msgid="6878021334497835259">"Προβολή πλέγματος"</string>
<string name="menu_list" msgid="7279285939892417279">"Προβολή λίστας"</string>
<string name="menu_sort" msgid="7677740407158414452">"Ταξινόμηση κατά"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Επιλογή όλων"</string>
<string name="menu_copy" msgid="3612326052677229148">"Αντιγραφή σε…"</string>
<string name="menu_move" msgid="1828090633118079817">"Μετακίνηση σε..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Νέο παράθυρο"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Αντιγραφή"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Επικόλληση"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Εμφ.εσωτ.χώρου αποθήκ."</string>
diff --git a/packages/DocumentsUI/res/values-en-rAU/strings.xml b/packages/DocumentsUI/res/values-en-rAU/strings.xml
index 26fd30ff9d38..e9ba34b47e04 100644
--- a/packages/DocumentsUI/res/values-en-rAU/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rAU/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Files"</string>
<string name="title_open" msgid="4353228937663917801">"Open from"</string>
<string name="title_save" msgid="2433679664882857999">"Save to"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Create folder"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"New folder"</string>
<string name="menu_grid" msgid="6878021334497835259">"Grid view"</string>
<string name="menu_list" msgid="7279285939892417279">"List view"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sort by"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Select all"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copy to…"</string>
<string name="menu_move" msgid="1828090633118079817">"Move to…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"New window"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copy"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Paste"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Show internal storage"</string>
diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml
index 26fd30ff9d38..e9ba34b47e04 100644
--- a/packages/DocumentsUI/res/values-en-rGB/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Files"</string>
<string name="title_open" msgid="4353228937663917801">"Open from"</string>
<string name="title_save" msgid="2433679664882857999">"Save to"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Create folder"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"New folder"</string>
<string name="menu_grid" msgid="6878021334497835259">"Grid view"</string>
<string name="menu_list" msgid="7279285939892417279">"List view"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sort by"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Select all"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copy to…"</string>
<string name="menu_move" msgid="1828090633118079817">"Move to…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"New window"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copy"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Paste"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Show internal storage"</string>
diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml
index 26fd30ff9d38..e9ba34b47e04 100644
--- a/packages/DocumentsUI/res/values-en-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Files"</string>
<string name="title_open" msgid="4353228937663917801">"Open from"</string>
<string name="title_save" msgid="2433679664882857999">"Save to"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Create folder"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"New folder"</string>
<string name="menu_grid" msgid="6878021334497835259">"Grid view"</string>
<string name="menu_list" msgid="7279285939892417279">"List view"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sort by"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Select all"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copy to…"</string>
<string name="menu_move" msgid="1828090633118079817">"Move to…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"New window"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copy"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Paste"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Show internal storage"</string>
diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml
index 6e805b90d4e4..8e0a18cc633d 100644
--- a/packages/DocumentsUI/res/values-es-rUS/strings.xml
+++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Archivos"</string>
<string name="title_open" msgid="4353228937663917801">"Abrir desde"</string>
<string name="title_save" msgid="2433679664882857999">"Guardar en"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Crear carpeta"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Carpeta nueva"</string>
<string name="menu_grid" msgid="6878021334497835259">"Vista de cuadrícula"</string>
<string name="menu_list" msgid="7279285939892417279">"Vista de lista"</string>
<string name="menu_sort" msgid="7677740407158414452">"Ordenar por"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Seleccionar todo"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copiar a…"</string>
<string name="menu_move" msgid="1828090633118079817">"Mover a…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Ventana nueva"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiar"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Pegar"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar almacen. interno"</string>
diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml
index 8fa1d2ed4c63..cf0b607a547b 100644
--- a/packages/DocumentsUI/res/values-es/strings.xml
+++ b/packages/DocumentsUI/res/values-es/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Archivos"</string>
<string name="title_open" msgid="4353228937663917801">"Abrir desde"</string>
<string name="title_save" msgid="2433679664882857999">"Guardar en"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Crear carpeta"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nueva carpeta"</string>
<string name="menu_grid" msgid="6878021334497835259">"Vista de cuadrícula"</string>
<string name="menu_list" msgid="7279285939892417279">"Vista de lista"</string>
<string name="menu_sort" msgid="7677740407158414452">"Ordenar por"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Seleccionar todo"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copiar en…"</string>
<string name="menu_move" msgid="1828090633118079817">"Mover a…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nueva ventana"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiar"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Pegar"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar almac. interno"</string>
diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml
index 7008885bd348..76d1f48e5a06 100644
--- a/packages/DocumentsUI/res/values-et-rEE/strings.xml
+++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Failid"</string>
<string name="title_open" msgid="4353228937663917801">"Ava:"</string>
<string name="title_save" msgid="2433679664882857999">"Salvesta:"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Loo kaust"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Uus kaust"</string>
<string name="menu_grid" msgid="6878021334497835259">"Ruudustikkuva"</string>
<string name="menu_list" msgid="7279285939892417279">"Loendikuva"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sortimisalus:"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Vali kõik"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopeeri asukohta ..."</string>
<string name="menu_move" msgid="1828090633118079817">"Teisaldamine kohta ..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Uus aken"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopeeri"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Kleebi"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Kuva sis. salvestusruum"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopeeri"</string>
<string name="button_move" msgid="2202666023104202232">"Teisalda"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Loobu"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Proovi uuesti"</string>
<string name="sort_name" msgid="9183560467917256779">"Nime järgi"</string>
<string name="sort_date" msgid="586080032956151448">"Muutmiskuupäeva järgi"</string>
<string name="sort_size" msgid="3350681319735474741">"Suuruse järgi"</string>
diff --git a/packages/DocumentsUI/res/values-eu-rES/strings.xml b/packages/DocumentsUI/res/values-eu-rES/strings.xml
index 17d7c660a1bb..f24cf6027bee 100644
--- a/packages/DocumentsUI/res/values-eu-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-eu-rES/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Fitxategiak"</string>
<string name="title_open" msgid="4353228937663917801">"Ireki hemendik"</string>
<string name="title_save" msgid="2433679664882857999">"Gorde hemen"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Sortu karpeta"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Karpeta berria"</string>
<string name="menu_grid" msgid="6878021334497835259">"Sareta-ikuspegia"</string>
<string name="menu_list" msgid="7279285939892417279">"Zerrenda-ikuspegia"</string>
<string name="menu_sort" msgid="7677740407158414452">"Ordenatzeko irizpidea"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Hautatu guztiak"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopiatu hemen…"</string>
<string name="menu_move" msgid="1828090633118079817">"Eraman hona…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Leiho berria"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiatu"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Itsatsi"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Erakutsi barneko memoria"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopiatu"</string>
<string name="button_move" msgid="2202666023104202232">"Mugitu"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Baztertu"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Saiatu berriro"</string>
<string name="sort_name" msgid="9183560467917256779">"Izenaren arabera"</string>
<string name="sort_date" msgid="586080032956151448">"Aldatze-dataren arabera"</string>
<string name="sort_size" msgid="3350681319735474741">"Tamainaren arabera"</string>
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index c4f6d588ea1c..b504db53b779 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"فایل‌ها"</string>
<string name="title_open" msgid="4353228937663917801">"باز کردن از"</string>
<string name="title_save" msgid="2433679664882857999">"ذخیره در"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"ایجاد پوشه"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"پوشه جدید"</string>
<string name="menu_grid" msgid="6878021334497835259">"نمای جدولی"</string>
<string name="menu_list" msgid="7279285939892417279">"نمای فهرستی"</string>
<string name="menu_sort" msgid="7677740407158414452">"مرتب‌سازی براساس"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"انتخاب همه"</string>
<string name="menu_copy" msgid="3612326052677229148">"کپی در..."</string>
<string name="menu_move" msgid="1828090633118079817">"انتقال به…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"پنجره جدید"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"کپی"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"جای‌گذاری"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"نمایش فضای ذخیره‌سازی داخلی"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"کپی"</string>
<string name="button_move" msgid="2202666023104202232">"انتقال"</string>
<string name="button_dismiss" msgid="3714065566893946085">"نپذیرفتن"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"دوباره امتحان کنید"</string>
<string name="sort_name" msgid="9183560467917256779">"براساس نام"</string>
<string name="sort_date" msgid="586080032956151448">"براساس تاریخ اصلاح"</string>
<string name="sort_size" msgid="3350681319735474741">"براساس اندازه"</string>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index 3e873009ce7a..33f2e3db54fb 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Tiedostot"</string>
<string name="title_open" msgid="4353228937663917801">"Avaa sijainnista"</string>
<string name="title_save" msgid="2433679664882857999">"Tallenna kohteeseen"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Luo kansio"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Uusi kansio"</string>
<string name="menu_grid" msgid="6878021334497835259">"Ruudukkonäkymä"</string>
<string name="menu_list" msgid="7279285939892417279">"Luettelonäkymä"</string>
<string name="menu_sort" msgid="7677740407158414452">"Lajitteluperuste"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Valitse kaikki"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopioi kohteeseen…"</string>
<string name="menu_move" msgid="1828090633118079817">"Siirrä kohteeseen…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Uusi ikkuna"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopioi"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Liitä"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Näytä sis. tallennustila"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopioi"</string>
<string name="button_move" msgid="2202666023104202232">"Siirrä"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Hylkää"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Yritä uudelleen"</string>
<string name="sort_name" msgid="9183560467917256779">"Nimen mukaan"</string>
<string name="sort_date" msgid="586080032956151448">"Muokkauspäivän mukaan"</string>
<string name="sort_size" msgid="3350681319735474741">"Koon mukaan"</string>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
index 3e82f3403cdc..228dc7a89555 100644
--- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml
+++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Fichiers"</string>
<string name="title_open" msgid="4353228937663917801">"Ouvrir à partir de"</string>
<string name="title_save" msgid="2433679664882857999">"Enregistrer dans"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Créer un dossier"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nouveau dossier"</string>
<string name="menu_grid" msgid="6878021334497835259">"Grille"</string>
<string name="menu_list" msgid="7279285939892417279">"Liste"</string>
<string name="menu_sort" msgid="7677740407158414452">"Trier par"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Tout sélectionner"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copier vers..."</string>
<string name="menu_move" msgid="1828090633118079817">"Déplacer dans…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nouvelle fenêtre"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copier"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Coller"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Aff. mém. stock. interne"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Copier"</string>
<string name="button_move" msgid="2202666023104202232">"Déplacer"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Faire disparaître"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Réessayer"</string>
<string name="sort_name" msgid="9183560467917256779">"Par nom"</string>
<string name="sort_date" msgid="586080032956151448">"Par date de modification"</string>
<string name="sort_size" msgid="3350681319735474741">"Par taille"</string>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index 0512ab15ae18..eb9f84ae937e 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Fichiers"</string>
<string name="title_open" msgid="4353228937663917801">"Ouvrir à partir de"</string>
<string name="title_save" msgid="2433679664882857999">"Enregistrer sous"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Créer un dossier"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nouveau dossier"</string>
<string name="menu_grid" msgid="6878021334497835259">"Grille"</string>
<string name="menu_list" msgid="7279285939892417279">"Liste"</string>
<string name="menu_sort" msgid="7677740407158414452">"Trier par"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Tout sélectionner"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copier vers…"</string>
<string name="menu_move" msgid="1828090633118079817">"Placer dans…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nouvelle fenêtre"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copier"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Coller"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Aff. mém. stock. interne"</string>
diff --git a/packages/DocumentsUI/res/values-gl-rES/strings.xml b/packages/DocumentsUI/res/values-gl-rES/strings.xml
index a1cd614f417e..07c5d3995a37 100644
--- a/packages/DocumentsUI/res/values-gl-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-gl-rES/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Ficheiros"</string>
<string name="title_open" msgid="4353228937663917801">"Abrir desde"</string>
<string name="title_save" msgid="2433679664882857999">"Gardar en"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Crear cartafol"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Novo cartafol"</string>
<string name="menu_grid" msgid="6878021334497835259">"Vista de grade"</string>
<string name="menu_list" msgid="7279285939892417279">"Vista de lista"</string>
<string name="menu_sort" msgid="7677740407158414452">"Ordenar por"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Seleccionar todo"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copiar en…"</string>
<string name="menu_move" msgid="1828090633118079817">"Mover a…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nova ventá"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiar"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Pegar"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar almacen. interno"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Copiar"</string>
<string name="button_move" msgid="2202666023104202232">"Mover"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Ignorar"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Tentar de novo"</string>
<string name="sort_name" msgid="9183560467917256779">"Por nome"</string>
<string name="sort_date" msgid="586080032956151448">"Por data de modificación"</string>
<string name="sort_size" msgid="3350681319735474741">"Por tamaño"</string>
diff --git a/packages/DocumentsUI/res/values-gu-rIN/strings.xml b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
index 059fb2cb2efb..dade7dff8492 100644
--- a/packages/DocumentsUI/res/values-gu-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-gu-rIN/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ફાઇલો"</string>
<string name="title_open" msgid="4353228937663917801">"અહીંથી ખોલો"</string>
<string name="title_save" msgid="2433679664882857999">"આમાં સાચવો"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"ફોલ્ડર બનાવો"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"નવું ફોલ્ડર"</string>
<string name="menu_grid" msgid="6878021334497835259">"ગ્રિડ દૃશ્ય"</string>
<string name="menu_list" msgid="7279285939892417279">"સૂચિ દૃશ્ય"</string>
<string name="menu_sort" msgid="7677740407158414452">"આ પ્રમાણે સૉર્ટ કરો"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"બધા પસંદ કરો"</string>
<string name="menu_copy" msgid="3612326052677229148">"આના પર કૉપિ કરો…"</string>
<string name="menu_move" msgid="1828090633118079817">"આમાં ખસેડો…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"નવી વિંડો"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"કૉપિ કરો"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"પેસ્ટ કરો"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"આંતરિક સ્ટોરેજ બતાવો"</string>
diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml
index 8e33a005f4cc..0a5de169b5fc 100644
--- a/packages/DocumentsUI/res/values-hi/strings.xml
+++ b/packages/DocumentsUI/res/values-hi/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"फ़ाइलें"</string>
<string name="title_open" msgid="4353228937663917801">"यहां से खोलें"</string>
<string name="title_save" msgid="2433679664882857999">"यहां सहेजें"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"फ़ोल्डर बनाएं"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"नया फ़ोल्डर"</string>
<string name="menu_grid" msgid="6878021334497835259">"ग्रिड दृश्य"</string>
<string name="menu_list" msgid="7279285939892417279">"सूची दृश्य"</string>
<string name="menu_sort" msgid="7677740407158414452">"इससे क्रमित करें"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"सभी चुनें"</string>
<string name="menu_copy" msgid="3612326052677229148">"इनकी कॉपी बनाएं..."</string>
<string name="menu_move" msgid="1828090633118079817">"इसमें ले जाएं…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"नई विंडो"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"कॉपी करें"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"चिपकाएं"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"आंतरिक मेमोरी दिखाएं"</string>
diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml
index 4774753aaf60..78c1485c7b6d 100644
--- a/packages/DocumentsUI/res/values-hr/strings.xml
+++ b/packages/DocumentsUI/res/values-hr/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Datoteke"</string>
<string name="title_open" msgid="4353228937663917801">"Otvori iz"</string>
<string name="title_save" msgid="2433679664882857999">"Spremi u"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Izradi mapu"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nova mapa"</string>
<string name="menu_grid" msgid="6878021334497835259">"Prikaz rešetke"</string>
<string name="menu_list" msgid="7279285939892417279">"Prikaz popisa"</string>
<string name="menu_sort" msgid="7677740407158414452">"Poredano po"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Odaberi sve"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopiraj u…"</string>
<string name="menu_move" msgid="1828090633118079817">"Premjesti u…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Novi prozor"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiraj"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Zalijepi"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Pokaži internu pohranu"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopiraj"</string>
<string name="button_move" msgid="2202666023104202232">"Premjesti"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Odbaci"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Pokušaj ponovo"</string>
<string name="sort_name" msgid="9183560467917256779">"Po nazivu"</string>
<string name="sort_date" msgid="586080032956151448">"Po datumu izmjene"</string>
<string name="sort_size" msgid="3350681319735474741">"Po veličini"</string>
diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml
index 7a521ecc3239..49481161d2e1 100644
--- a/packages/DocumentsUI/res/values-hu/strings.xml
+++ b/packages/DocumentsUI/res/values-hu/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Fájlok"</string>
<string name="title_open" msgid="4353228937663917801">"Megnyitás innen"</string>
<string name="title_save" msgid="2433679664882857999">"Mentés ide"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Mappa létrehozása"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Új mappa"</string>
<string name="menu_grid" msgid="6878021334497835259">"Rács"</string>
<string name="menu_list" msgid="7279285939892417279">"Lista"</string>
<string name="menu_sort" msgid="7677740407158414452">"Rendezés"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Összes kijelölése"</string>
<string name="menu_copy" msgid="3612326052677229148">"Másolás ide…"</string>
<string name="menu_move" msgid="1828090633118079817">"Áthelyezés…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Új ablak"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Másolás"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Beillesztés"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Belső tárhely"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Másolás"</string>
<string name="button_move" msgid="2202666023104202232">"Áthelyezés"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Elvetés"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Újrapróbálkozás"</string>
<string name="sort_name" msgid="9183560467917256779">"Név szerint"</string>
<string name="sort_date" msgid="586080032956151448">"Módosítás dátuma szerint"</string>
<string name="sort_size" msgid="3350681319735474741">"Méret szerint"</string>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
index 95d73f0e5b2e..6dc66b057f1a 100644
--- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml
+++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Ֆայլեր"</string>
<string name="title_open" msgid="4353228937663917801">"Բացել այստեղից"</string>
<string name="title_save" msgid="2433679664882857999">"Պահել այստեղ"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Ստեղծել պանակ"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Նոր պանակ"</string>
<string name="menu_grid" msgid="6878021334497835259">"Ցանցի տեսքով"</string>
<string name="menu_list" msgid="7279285939892417279">"Ցուցակի տեսք"</string>
<string name="menu_sort" msgid="7677740407158414452">"Դասավորել ըստ"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Ընտրել բոլորը"</string>
<string name="menu_copy" msgid="3612326052677229148">"Պատճենել…"</string>
<string name="menu_move" msgid="1828090633118079817">"Տեղափոխում դեպի…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Նոր պատուհան"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Պատճենել"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Տեղադրել"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ցույց տալ ներքին պահոցը"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Պատճենել"</string>
<string name="button_move" msgid="2202666023104202232">"Տեղափոխել"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Փակել"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Փորձել նորից"</string>
<string name="sort_name" msgid="9183560467917256779">"Ըստ անվան"</string>
<string name="sort_date" msgid="586080032956151448">"Ըստ փոփոխման ամսաթվի"</string>
<string name="sort_size" msgid="3350681319735474741">"Ըստ չափի"</string>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index 63d415cadf46..241248848981 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"File"</string>
<string name="title_open" msgid="4353228937663917801">"Buka dari"</string>
<string name="title_save" msgid="2433679664882857999">"Simpan ke"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Buat folder"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Folder baru"</string>
<string name="menu_grid" msgid="6878021334497835259">"Tampilan kisi"</string>
<string name="menu_list" msgid="7279285939892417279">"Tampilan daftar"</string>
<string name="menu_sort" msgid="7677740407158414452">"Urutkan menurut"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Pilih semua"</string>
<string name="menu_copy" msgid="3612326052677229148">"Salin ke…"</string>
<string name="menu_move" msgid="1828090633118079817">"Pindahkan ke..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Jendela baru"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Salin"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Tempel"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Tampilkan simpanan internal"</string>
diff --git a/packages/DocumentsUI/res/values-is-rIS/strings.xml b/packages/DocumentsUI/res/values-is-rIS/strings.xml
index 0c0e47fd4716..d09a44bda2d2 100644
--- a/packages/DocumentsUI/res/values-is-rIS/strings.xml
+++ b/packages/DocumentsUI/res/values-is-rIS/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Skrár"</string>
<string name="title_open" msgid="4353228937663917801">"Opna frá"</string>
<string name="title_save" msgid="2433679664882857999">"Vista í"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Búa til möppu"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Ný mappa"</string>
<string name="menu_grid" msgid="6878021334497835259">"Töfluyfirlit"</string>
<string name="menu_list" msgid="7279285939892417279">"Listayfirlit"</string>
<string name="menu_sort" msgid="7677740407158414452">"Raða eftir"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Velja allt"</string>
<string name="menu_copy" msgid="3612326052677229148">"Afrita í ..."</string>
<string name="menu_move" msgid="1828090633118079817">"Færa í…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nýr gluggi"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Afrita"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Líma"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Sýna innbyggða geymslu"</string>
diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml
index de129a7ec0cf..48242055f5e2 100644
--- a/packages/DocumentsUI/res/values-it/strings.xml
+++ b/packages/DocumentsUI/res/values-it/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"File"</string>
<string name="title_open" msgid="4353228937663917801">"Apri da"</string>
<string name="title_save" msgid="2433679664882857999">"Salva in"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Crea cartella"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nuova cartella"</string>
<string name="menu_grid" msgid="6878021334497835259">"Visualizzazione griglia"</string>
<string name="menu_list" msgid="7279285939892417279">"Visualizzazione elenco"</string>
<string name="menu_sort" msgid="7677740407158414452">"Ordina per"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Seleziona tutto"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copia in…"</string>
<string name="menu_move" msgid="1828090633118079817">"Sposta in..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nuova finestra"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copia"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Incolla"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostra memoria interna"</string>
diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml
index dbe463080c82..1a1951029aac 100644
--- a/packages/DocumentsUI/res/values-iw/strings.xml
+++ b/packages/DocumentsUI/res/values-iw/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"קבצים"</string>
<string name="title_open" msgid="4353228937663917801">"פתח מ-"</string>
<string name="title_save" msgid="2433679664882857999">"שמור ב-"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"צור תיקיה"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"תיקייה חדשה"</string>
<string name="menu_grid" msgid="6878021334497835259">"תצוגת רשת"</string>
<string name="menu_list" msgid="7279285939892417279">"תצוגת רשימה"</string>
<string name="menu_sort" msgid="7677740407158414452">"מיין לפי"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"בחר הכל"</string>
<string name="menu_copy" msgid="3612326052677229148">"העתק אל…"</string>
<string name="menu_move" msgid="1828090633118079817">"העברה אל…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"חלון חדש"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"העתק"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"הדבק"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"הצג אחסון פנימי"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"העתק"</string>
<string name="button_move" msgid="2202666023104202232">"העבר"</string>
<string name="button_dismiss" msgid="3714065566893946085">"הסר"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"נסה שוב"</string>
<string name="sort_name" msgid="9183560467917256779">"לפי שם"</string>
<string name="sort_date" msgid="586080032956151448">"לפי תאריך שינוי"</string>
<string name="sort_size" msgid="3350681319735474741">"לפי גודל"</string>
diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml
index 45f0ea1df6f8..7e356af51ccf 100644
--- a/packages/DocumentsUI/res/values-ja/strings.xml
+++ b/packages/DocumentsUI/res/values-ja/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ファイル"</string>
<string name="title_open" msgid="4353228937663917801">"次から開く:"</string>
<string name="title_save" msgid="2433679664882857999">"次に保存:"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"フォルダを作成"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"新しいフォルダ"</string>
<string name="menu_grid" msgid="6878021334497835259">"グリッド表示"</string>
<string name="menu_list" msgid="7279285939892417279">"リスト表示"</string>
<string name="menu_sort" msgid="7677740407158414452">"並べ替え"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"すべて選択"</string>
<string name="menu_copy" msgid="3612326052677229148">"コピー…"</string>
<string name="menu_move" msgid="1828090633118079817">"移動..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"新しいウィンドウ"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"コピー"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"貼り付け"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"内部ストレージを表示"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"コピー"</string>
<string name="button_move" msgid="2202666023104202232">"移動"</string>
<string name="button_dismiss" msgid="3714065566893946085">"表示しない"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"再試行"</string>
<string name="sort_name" msgid="9183560467917256779">"名前順"</string>
<string name="sort_date" msgid="586080032956151448">"更新日順"</string>
<string name="sort_size" msgid="3350681319735474741">"サイズ順"</string>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
index 114e73c75cb5..6e880aa9453f 100644
--- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml
+++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ფაილები"</string>
<string name="title_open" msgid="4353228937663917801">"გახსნა აქედან:"</string>
<string name="title_save" msgid="2433679664882857999">"შენახვა აქ:"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"საქაღალდის შექმნა"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"ახალი საქაღალდე"</string>
<string name="menu_grid" msgid="6878021334497835259">"ბადის ხედი"</string>
<string name="menu_list" msgid="7279285939892417279">"სიის ხედი"</string>
<string name="menu_sort" msgid="7677740407158414452">"სორტირება:"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"ყველას არჩევა"</string>
<string name="menu_copy" msgid="3612326052677229148">"კოპირება…"</string>
<string name="menu_move" msgid="1828090633118079817">"გადაადგილება..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"ახალი ფანჯარა"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"კოპირება"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ჩასმა"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"შიდა საცავის ჩვენება"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"კოპირება"</string>
<string name="button_move" msgid="2202666023104202232">"გადაადგილება"</string>
<string name="button_dismiss" msgid="3714065566893946085">"დახურვა"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"ისევ ცდა"</string>
<string name="sort_name" msgid="9183560467917256779">"სახელით"</string>
<string name="sort_date" msgid="586080032956151448">"ცვლილების თარიღით"</string>
<string name="sort_size" msgid="3350681319735474741">"ზომით"</string>
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
index af123c62b20c..0d553e0c6843 100644
--- a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
+++ b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Файлдар"</string>
<string name="title_open" msgid="4353228937663917801">"Мынадан ашу:"</string>
<string name="title_save" msgid="2433679664882857999">"Сақталатын орны"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Қалта жасақтау"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Жаңа қалта"</string>
<string name="menu_grid" msgid="6878021334497835259">"Торлы көрініс"</string>
<string name="menu_list" msgid="7279285939892417279">"Тізім көрінісі"</string>
<string name="menu_sort" msgid="7677740407158414452">"Белгіге қарай сұрыптау"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Барлығын таңдау"</string>
<string name="menu_copy" msgid="3612326052677229148">"Көшіру орны…"</string>
<string name="menu_move" msgid="1828090633118079817">"Орнын ауыстыру…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Жаңа терезе"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Көшіру"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Қою"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ішкі жадты көрсету"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Көшіру"</string>
<string name="button_move" msgid="2202666023104202232">"Жылжыту"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Өшіру"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Әрекетті қайталау"</string>
<string name="sort_name" msgid="9183560467917256779">"Атауы бойынша"</string>
<string name="sort_date" msgid="586080032956151448">"Өзгертілген мерзімі бойынша"</string>
<string name="sort_size" msgid="3350681319735474741">"Өлшемі бойынша"</string>
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 8f3feaedefd1..46808794fb66 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ឯកសារ"</string>
<string name="title_open" msgid="4353228937663917801">"បើក​ពី"</string>
<string name="title_save" msgid="2433679664882857999">"រក្សា​ទុក​ទៅ"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"បង្កើត​ថត"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"ថត​ថ្មី"</string>
<string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាព​ក្រឡា​"</string>
<string name="menu_list" msgid="7279285939892417279">"ទិដ្ឋភាព​បញ្ជី"</string>
<string name="menu_sort" msgid="7677740407158414452">"តម្រៀប​តាម"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"ជ្រើសរើសទាំងអស់"</string>
<string name="menu_copy" msgid="3612326052677229148">"ថតចម្លងទៅ…"</string>
<string name="menu_move" msgid="1828090633118079817">"ផ្លាស់ទីទៅ៖"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"បង្អួចថ្មី"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"ចម្លង"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"បិទភ្ជាប់"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"បង្ហាញឧបករណ៍ផ្ទុកខាងក្នុង"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"ចម្លង"</string>
<string name="button_move" msgid="2202666023104202232">"ផ្លាស់ទី"</string>
<string name="button_dismiss" msgid="3714065566893946085">"បដិសេធ"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"ព្យាយាមម្ដងទៀត"</string>
<string name="sort_name" msgid="9183560467917256779">"តាម​ឈ្មោះ"</string>
<string name="sort_date" msgid="586080032956151448">"តាម​កាលបរិច្ឆេទ​បាន​កែប្រែ"</string>
<string name="sort_size" msgid="3350681319735474741">"តាម​​ទំហំ"</string>
diff --git a/packages/DocumentsUI/res/values-kn-rIN/strings.xml b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
index 826cf1e3fa73..7d5770abd768 100644
--- a/packages/DocumentsUI/res/values-kn-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ಫೈಲ್‌ಗಳು"</string>
<string name="title_open" msgid="4353228937663917801">"ಇದರ ಮೂಲಕ ತೆರೆಯಿರಿ"</string>
<string name="title_save" msgid="2433679664882857999">"ಇವುಗಳಲ್ಲಿ ಉಳಿಸಿ"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"ಫೋಲ್ಡರ್ ರಚಿಸು"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"ಹೊಸ ಫೋಲ್ಡರ್"</string>
<string name="menu_grid" msgid="6878021334497835259">"ಗ್ರಿಡ್ ವೀಕ್ಷಣೆ"</string>
<string name="menu_list" msgid="7279285939892417279">"ಪಟ್ಟಿ ವೀಕ್ಷಣೆ"</string>
<string name="menu_sort" msgid="7677740407158414452">"ಈ ಪ್ರಕಾರ ವಿಂಗಡಿಸು"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"ಎಲ್ಲವನ್ನೂ ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="menu_copy" msgid="3612326052677229148">"ಇದಕ್ಕೆ ನಕಲಿಸಿ…"</string>
<string name="menu_move" msgid="1828090633118079817">"ಇದಕ್ಕೆ ಸರಿಸು…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"ಹೊಸ ವಿಂಡೋ"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"ನಕಲಿಸು"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ಅಂಟಿಸು"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ಆಂತರಿಕ ಸಂಗ್ರಹಣೆಯನ್ನು ತೋರಿಸು"</string>
diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml
index 322b43b3bc3a..3032eede81ec 100644
--- a/packages/DocumentsUI/res/values-ko/strings.xml
+++ b/packages/DocumentsUI/res/values-ko/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"파일"</string>
<string name="title_open" msgid="4353228937663917801">"열기:"</string>
<string name="title_save" msgid="2433679664882857999">"저장 위치:"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"폴더 만들기"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"새 폴더"</string>
<string name="menu_grid" msgid="6878021334497835259">"바둑판식 보기"</string>
<string name="menu_list" msgid="7279285939892417279">"목록 보기"</string>
<string name="menu_sort" msgid="7677740407158414452">"정렬 기준"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"모두 선택"</string>
<string name="menu_copy" msgid="3612326052677229148">"복사…"</string>
<string name="menu_move" msgid="1828090633118079817">"이동…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"새 창"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"복사"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"붙여넣기"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"내부 저장소 표시"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"복사"</string>
<string name="button_move" msgid="2202666023104202232">"이동"</string>
<string name="button_dismiss" msgid="3714065566893946085">"닫기"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"다시 시도"</string>
<string name="sort_name" msgid="9183560467917256779">"이름순"</string>
<string name="sort_date" msgid="586080032956151448">"수정된 날짜순"</string>
<string name="sort_size" msgid="3350681319735474741">"크기순"</string>
diff --git a/packages/DocumentsUI/res/values-ky-rKG/strings.xml b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
index c8aa69eb3c18..14a25bcfc377 100644
--- a/packages/DocumentsUI/res/values-ky-rKG/strings.xml
+++ b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Файлдар"</string>
<string name="title_open" msgid="4353228937663917801">"Кийинкиден ачуу:"</string>
<string name="title_save" msgid="2433679664882857999">"Кийинкиге сактоо:"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Папка түзүү"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Жаңы куржун"</string>
<string name="menu_grid" msgid="6878021334497835259">"Тор көрүнүшү"</string>
<string name="menu_list" msgid="7279285939892417279">"Тизмек көрүнүшү"</string>
<string name="menu_sort" msgid="7677740407158414452">"Ылгоо"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Бардыгын тандоо"</string>
<string name="menu_copy" msgid="3612326052677229148">"Төмөнкүгө көчүрүү…"</string>
<string name="menu_move" msgid="1828090633118079817">"Төмөнкүгө жылдыруу..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Жаңы терезе"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Көчүрүү"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Чаптоо"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ички сактагычты көрсөтүү"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Көчүрүү"</string>
<string name="button_move" msgid="2202666023104202232">"Жылдыруу"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Этибарга албоо"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Дагы аракет кылыңыз"</string>
<string name="sort_name" msgid="9183560467917256779">"Аты боюнча"</string>
<string name="sort_date" msgid="586080032956151448">"Өзгөртүлгөн күнү боюнча"</string>
<string name="sort_size" msgid="3350681319735474741">"Өлчөмү боюнча"</string>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
index dedfa0552b36..2a680bf50f7d 100644
--- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml
+++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"​ໄຟລ໌"</string>
<string name="title_open" msgid="4353228937663917801">"ເປີດ​ຈາກ"</string>
<string name="title_save" msgid="2433679664882857999">"ບັນທຶກໄປທີ່"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"ສ້າງໂຟນເດີ"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"ໂຟ​ລ​ເດີໃໝ່"</string>
<string name="menu_grid" msgid="6878021334497835259">"ມຸມມອງແບບຊ່ອງ"</string>
<string name="menu_list" msgid="7279285939892417279">"ມຸມມອງແບບລາຍຊື່"</string>
<string name="menu_sort" msgid="7677740407158414452">"ຮຽງລຳດັບຕາມ"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"ເລືອກທັງຫມົດ"</string>
<string name="menu_copy" msgid="3612326052677229148">"ອັດ​ສຳ​ເນົາ​ໃສ່…"</string>
<string name="menu_move" msgid="1828090633118079817">"ຍ້າຍໄປໃສ່..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"ໜ້າຈໍໃໝ່"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"ສຳເນົາ"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"​ວາງໃສ່"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ສະແດງ​ໂຕເກັບ​ຂໍ້ມູນພາຍໃນ"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"ສຳເນົາ"</string>
<string name="button_move" msgid="2202666023104202232">"ຍ້າຍ"</string>
<string name="button_dismiss" msgid="3714065566893946085">"ປິດໄວ້"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"​ລອງ​ໃໝ່​ອີກ​"</string>
<string name="sort_name" msgid="9183560467917256779">"ຕາມຊື່"</string>
<string name="sort_date" msgid="586080032956151448">"ຕາມວັນທີທີ່ແກ້ໄຂ"</string>
<string name="sort_size" msgid="3350681319735474741">"ຕາມຂະໜາດ"</string>
diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml
index 0c8b44388e00..8105846f605d 100644
--- a/packages/DocumentsUI/res/values-lt/strings.xml
+++ b/packages/DocumentsUI/res/values-lt/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Failai"</string>
<string name="title_open" msgid="4353228937663917801">"Atidaryti iš"</string>
<string name="title_save" msgid="2433679664882857999">"Išsaugoti į"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Kurti aplanką"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Naujas aplankas"</string>
<string name="menu_grid" msgid="6878021334497835259">"Tinklelio rodinys"</string>
<string name="menu_list" msgid="7279285939892417279">"Sąrašo rodinys"</string>
<string name="menu_sort" msgid="7677740407158414452">"Rūšiuoti pagal"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Pasirinkti viską"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopijuoti į..."</string>
<string name="menu_move" msgid="1828090633118079817">"Perkelti į…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Naujas langas"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopijuoti"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Įklijuoti"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Rodyti vidinę atmintį"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopijuoti"</string>
<string name="button_move" msgid="2202666023104202232">"Perkelti"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Atsisakyti"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Bandyti dar kartą"</string>
<string name="sort_name" msgid="9183560467917256779">"Pagal pavadinimą"</string>
<string name="sort_date" msgid="586080032956151448">"Pagal keitimo datą"</string>
<string name="sort_size" msgid="3350681319735474741">"Pagal dydį"</string>
diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml
index 5c79554424f7..48715e6142c7 100644
--- a/packages/DocumentsUI/res/values-lv/strings.xml
+++ b/packages/DocumentsUI/res/values-lv/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Faili"</string>
<string name="title_open" msgid="4353228937663917801">"Atvēršana no:"</string>
<string name="title_save" msgid="2433679664882857999">"Saglabāšana:"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Izveidot mapi"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Jauna mape"</string>
<string name="menu_grid" msgid="6878021334497835259">"Režģis"</string>
<string name="menu_list" msgid="7279285939892417279">"Saraksts"</string>
<string name="menu_sort" msgid="7677740407158414452">"Kārtot pēc"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Atlasīt visus"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopēt…"</string>
<string name="menu_move" msgid="1828090633118079817">"Pārvietot uz…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Jauns logs"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopēt"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Ielīmēt"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Rādīt iekšējo atmiņu"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopēt"</string>
<string name="button_move" msgid="2202666023104202232">"Pārvietot"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Noraidīt"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Mēģināt vēlreiz"</string>
<string name="sort_name" msgid="9183560467917256779">"Pēc nosaukuma"</string>
<string name="sort_date" msgid="586080032956151448">"Pēc pārveidošanas datuma"</string>
<string name="sort_size" msgid="3350681319735474741">"Pēc lieluma"</string>
diff --git a/packages/DocumentsUI/res/values-mk-rMK/strings.xml b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
index b2cb9f1ca2ea..950a9b1c7c07 100644
--- a/packages/DocumentsUI/res/values-mk-rMK/strings.xml
+++ b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Датотеки"</string>
<string name="title_open" msgid="4353228937663917801">"Отвори од"</string>
<string name="title_save" msgid="2433679664882857999">"Зачувај во"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Создади папка"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Нова папка"</string>
<string name="menu_grid" msgid="6878021334497835259">"Приказ на мрежа"</string>
<string name="menu_list" msgid="7279285939892417279">"Приказ на список"</string>
<string name="menu_sort" msgid="7677740407158414452">"Подреди по"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Избери ги сите"</string>
<string name="menu_copy" msgid="3612326052677229148">"Копирај во…"</string>
<string name="menu_move" msgid="1828090633118079817">"Премести во..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Нов прозорец"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Копирај"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Залепи"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Прикажи внатрешна мемор."</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Копирај"</string>
<string name="button_move" msgid="2202666023104202232">"Премести"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Отфрли"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Обиди се пак"</string>
<string name="sort_name" msgid="9183560467917256779">"По име"</string>
<string name="sort_date" msgid="586080032956151448">"Изменети по датум"</string>
<string name="sort_size" msgid="3350681319735474741">"По големина"</string>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
index 07efa66bc871..32411b2097a8 100644
--- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ഫയലുകൾ"</string>
<string name="title_open" msgid="4353228937663917801">"ഇതിൽ നിന്നും തുറക്കുക"</string>
<string name="title_save" msgid="2433679664882857999">"ഇതില്‍‌ സംരക്ഷിക്കുക"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"ഫോൾഡർ സൃഷ്‌ടിക്കുക"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"പുതിയ ഫോൾഡർ"</string>
<string name="menu_grid" msgid="6878021334497835259">"ഗ്രിഡ് കാഴ്‌ച"</string>
<string name="menu_list" msgid="7279285939892417279">"ലിസ്റ്റ് കാഴ്‌ച"</string>
<string name="menu_sort" msgid="7677740407158414452">"ഇപ്രകാരം അടുക്കുക"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"എല്ലാം തിരഞ്ഞെടുക്കുക"</string>
<string name="menu_copy" msgid="3612326052677229148">"ഇതിൽ പകർത്തുക…"</string>
<string name="menu_move" msgid="1828090633118079817">"ഇതിലേക്ക് നീക്കുക..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"പുതിയ വിന്‍‍ഡോ"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"പകര്‍ത്തുക"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ഒട്ടിക്കുക"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ആന്തരിക സ്റ്റോറേജ് കാണിക്കുക"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"പകര്‍ത്തുക"</string>
<string name="button_move" msgid="2202666023104202232">"നീക്കുക"</string>
<string name="button_dismiss" msgid="3714065566893946085">"ഡിസ്മിസ് ചെയ്യുക"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"വീണ്ടും ശ്രമിക്കുക"</string>
<string name="sort_name" msgid="9183560467917256779">"പേര് പ്രകാരം"</string>
<string name="sort_date" msgid="586080032956151448">"പരിഷ്‌ക്കരിച്ച തീയതി പ്രകാരം"</string>
<string name="sort_size" msgid="3350681319735474741">"വലുപ്പം പ്രകാരം"</string>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
index d45778cab5bd..0c5c72db314a 100644
--- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml
+++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Файл"</string>
<string name="title_open" msgid="4353228937663917801">"Нээх"</string>
<string name="title_save" msgid="2433679664882857999">"Хадгалах"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Фолдер үүсгэх"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Шинэ фолдер"</string>
<string name="menu_grid" msgid="6878021334497835259">"Эгнүүлж харах"</string>
<string name="menu_list" msgid="7279285939892417279">"Жагсааж харах"</string>
<string name="menu_sort" msgid="7677740407158414452">"Эрэмбэлэх"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Бүгдийг сонгох"</string>
<string name="menu_copy" msgid="3612326052677229148">"...руу хуулах"</string>
<string name="menu_move" msgid="1828090633118079817">"Байршуулах газар"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Шинэ цонх"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Хуулах"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Буулгах"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Дотоод санг харуулах"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Хуулах"</string>
<string name="button_move" msgid="2202666023104202232">"Зөөх"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Алгасах"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Дахин оролдоно уу"</string>
<string name="sort_name" msgid="9183560467917256779">"Нэрээр"</string>
<string name="sort_date" msgid="586080032956151448">"Өөрчлөгдсөн огноогоор"</string>
<string name="sort_size" msgid="3350681319735474741">"Хэмжээгээр"</string>
diff --git a/packages/DocumentsUI/res/values-mr-rIN/strings.xml b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
index 8d70143ffc0a..15c1291d9205 100644
--- a/packages/DocumentsUI/res/values-mr-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"फायली"</string>
<string name="title_open" msgid="4353228937663917801">"वरून उघडा"</string>
<string name="title_save" msgid="2433679664882857999">"येथे जतन करा"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"फोल्डर तयार करा"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"नवीन फोल्डर"</string>
<string name="menu_grid" msgid="6878021334497835259">"ग्रिड दृश्य"</string>
<string name="menu_list" msgid="7279285939892417279">"सूची"</string>
<string name="menu_sort" msgid="7677740407158414452">"नुसार क्रमवारी लावा"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"सर्व निवडा"</string>
<string name="menu_copy" msgid="3612326052677229148">"यावर कॉपी करा…"</string>
<string name="menu_move" msgid="1828090633118079817">"यावर हलवा…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"नवीन विंडो"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"कॉपी करा"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"पेस्ट करा"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"अंतर्गत संचयन दर्शवा"</string>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
index 22501095b614..12cf2e3f0472 100644
--- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml
+++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Fail"</string>
<string name="title_open" msgid="4353228937663917801">"Buka dari"</string>
<string name="title_save" msgid="2433679664882857999">"Simpan ke"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Buat folder"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Folder baharu"</string>
<string name="menu_grid" msgid="6878021334497835259">"Paparan grid"</string>
<string name="menu_list" msgid="7279285939892417279">"Paparan senarai"</string>
<string name="menu_sort" msgid="7677740407158414452">"Isih mengikut"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Pilih semua"</string>
<string name="menu_copy" msgid="3612326052677229148">"Salin ke..."</string>
<string name="menu_move" msgid="1828090633118079817">"Alihkan ke…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Tetingkap baharu"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Salin"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Tampal"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Papar storan dalaman"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Salin"</string>
<string name="button_move" msgid="2202666023104202232">"Alihkan"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Tolak"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Cuba Lagi"</string>
<string name="sort_name" msgid="9183560467917256779">"Mengikut nama"</string>
<string name="sort_date" msgid="586080032956151448">"Mengikut tarikh diubah"</string>
<string name="sort_size" msgid="3350681319735474741">"Mengikut saiz"</string>
diff --git a/packages/DocumentsUI/res/values-my-rMM/strings.xml b/packages/DocumentsUI/res/values-my-rMM/strings.xml
index 480c27b1ddfb..a1ab012c91ff 100644
--- a/packages/DocumentsUI/res/values-my-rMM/strings.xml
+++ b/packages/DocumentsUI/res/values-my-rMM/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ဖိုင်များ"</string>
<string name="title_open" msgid="4353228937663917801">"မှ ဖွင့်ပါ"</string>
<string name="title_save" msgid="2433679664882857999">"သို့ သိမ်းပါ"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"အကန့် တည်ဆောက်ရန်"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"ဖိုလ်ဒါ အသစ်"</string>
<string name="menu_grid" msgid="6878021334497835259">"ဖယားကွက်မြင်ကွင်း"</string>
<string name="menu_list" msgid="7279285939892417279">"အစဉ်လိုက်မြင်ကွင်း"</string>
<string name="menu_sort" msgid="7677740407158414452">"အစဉ်အလိုက် စီခြင်း"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"အားလုံးကို ရွေးရန်"</string>
<string name="menu_copy" msgid="3612326052677229148">"သို့ကူးယူရန်…"</string>
<string name="menu_move" msgid="1828090633118079817">"...သို့ ရွှေ့ရန်"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"ဝင်းဒိုးသစ်"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"ကူးယူရန်"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ကပ်ရန်"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"စက်ရှိစတိုရုံ ပြပါ"</string>
diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml
index 48355aab72ba..edeaa6d0f1e5 100644
--- a/packages/DocumentsUI/res/values-nb/strings.xml
+++ b/packages/DocumentsUI/res/values-nb/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Filer"</string>
<string name="title_open" msgid="4353228937663917801">"Åpne fra"</string>
<string name="title_save" msgid="2433679664882857999">"Lagre i"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Opprett en mappe"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Ny mappe"</string>
<string name="menu_grid" msgid="6878021334497835259">"Rutenettvisning"</string>
<string name="menu_list" msgid="7279285939892417279">"Listevisning"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sortér etter"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Markér alt"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopiér til …"</string>
<string name="menu_move" msgid="1828090633118079817">"Flytt til"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nytt vindu"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiér"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Lim inn"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Vis den interne lagringen"</string>
diff --git a/packages/DocumentsUI/res/values-ne-rNP/strings.xml b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
index 53942c192ac3..bd5421155862 100644
--- a/packages/DocumentsUI/res/values-ne-rNP/strings.xml
+++ b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"फाइलहरू"</string>
<string name="title_open" msgid="4353228937663917801">"यसबाट खोल्नुहोस्"</string>
<string name="title_save" msgid="2433679664882857999">"यसमा सुरक्षित गर्नुहोस्"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"फोल्डर सिर्जना गर्नुहोस्"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"नयाँ फोल्डर"</string>
<string name="menu_grid" msgid="6878021334497835259">"ग्रिड दृश्य"</string>
<string name="menu_list" msgid="7279285939892417279">"सूची दृश्य"</string>
<string name="menu_sort" msgid="7677740407158414452">"यसद्वारा क्रमवद्घ गर्नुहोस्"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"सबै चयन गर्नुहोस्"</string>
<string name="menu_copy" msgid="3612326052677229148">"यसमा प्रतिलिपि गर्नुहोस् ..."</string>
<string name="menu_move" msgid="1828090633118079817">"…मा सार्नुहोस्"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"नयाँ विन्डो"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"प्रतिलिपि बनाउनुहोस्"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"टाँस्नुहोस्"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"आन्तरिक भण्डारण देखाउनुहोस्"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"प्रतिलिपि बनाउनुहोस्"</string>
<string name="button_move" msgid="2202666023104202232">"सार्नुहोस्"</string>
<string name="button_dismiss" msgid="3714065566893946085">"खारेज गर्नुहोस्"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"पुन: प्रयास गर्नुहोस्"</string>
<string name="sort_name" msgid="9183560467917256779">"नाम अनुसार"</string>
<string name="sort_date" msgid="586080032956151448">"परिमार्जित मिति अनुसार"</string>
<string name="sort_size" msgid="3350681319735474741">"आकार अनुसार"</string>
diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml
index d3e6bbe9cae2..f2eda72d5450 100644
--- a/packages/DocumentsUI/res/values-nl/strings.xml
+++ b/packages/DocumentsUI/res/values-nl/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Bestanden"</string>
<string name="title_open" msgid="4353228937663917801">"Openen vanuit"</string>
<string name="title_save" msgid="2433679664882857999">"Opslaan in"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Map maken"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nieuwe map"</string>
<string name="menu_grid" msgid="6878021334497835259">"Rasterweergave"</string>
<string name="menu_list" msgid="7279285939892417279">"Lijstweergave"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sorteren op"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Alles selecteren"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopiëren naar…"</string>
<string name="menu_move" msgid="1828090633118079817">"Verplaatsen naar…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nieuw venster"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiëren"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Plakken"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Interne opslag weergeven"</string>
diff --git a/packages/DocumentsUI/res/values-pa-rIN/strings.xml b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
index 3b1439653708..e139dfdcaa12 100644
--- a/packages/DocumentsUI/res/values-pa-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-pa-rIN/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ਫਾਈਲਾਂ"</string>
<string name="title_open" msgid="4353228937663917801">"ਤੋਂ ਖੋਲ੍ਹੋ"</string>
<string name="title_save" msgid="2433679664882857999">"ਇਸ ਵਿੱਚ ਸੁਰੱਖਿਅਤ ਕਰੋ"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"ਫੋਲਡਰ ਬਣਾਓ"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"ਨਵਾਂ ਫੋਲਡਰ"</string>
<string name="menu_grid" msgid="6878021334497835259">"ਗ੍ਰਿਡ ਵਿਊ"</string>
<string name="menu_list" msgid="7279285939892417279">"ਸੂਚੀ ਦ੍ਰਿਸ਼"</string>
<string name="menu_sort" msgid="7677740407158414452">"ਇਸ ਅਨੁਸਾਰ ਛਾਂਟੋ"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"ਸਾਰੇ ਚੁਣੋ"</string>
<string name="menu_copy" msgid="3612326052677229148">"ਇਸ ਵਿੱਚ ਕਾਪੀ ਕਰੋ…"</string>
<string name="menu_move" msgid="1828090633118079817">"ਇਸ ਵਿੱਚ ਮੂਵ ਕਰੋ..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"ਨਵੀਂ ਵਿੰਡੋ"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"ਕਾਪੀ ਕਰੋ"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ਪੇਸਟ ਕਰੋ"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ਅੰਦਰੂਨੀ ਸਟੋਰੇਜ ਦਿਖਾਓ"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"ਕਾਪੀ ਕਰੋ"</string>
<string name="button_move" msgid="2202666023104202232">"ਮੂਵ ਕਰੋ"</string>
<string name="button_dismiss" msgid="3714065566893946085">"ਬਰਖਾਸਤ ਕਰੋ"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="sort_name" msgid="9183560467917256779">"ਨਾਮ ਮੁਤਾਬਕ"</string>
<string name="sort_date" msgid="586080032956151448">"ਤਾਰੀਖ ਮੁਤਾਬਕ ਸੰਸ਼ੋਧਿਤ"</string>
<string name="sort_size" msgid="3350681319735474741">"ਆਕਾਰ ਮੁਤਾਬਕ"</string>
diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml
index 3e4ef6837726..bb81377e5ec0 100644
--- a/packages/DocumentsUI/res/values-pl/strings.xml
+++ b/packages/DocumentsUI/res/values-pl/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Pliki"</string>
<string name="title_open" msgid="4353228937663917801">"Otwórz z"</string>
<string name="title_save" msgid="2433679664882857999">"Zapisz w"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Utwórz folder"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nowy folder"</string>
<string name="menu_grid" msgid="6878021334497835259">"Widok siatki"</string>
<string name="menu_list" msgid="7279285939892417279">"Widok listy"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sortuj według"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Zaznacz wszystko"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopiuj do…"</string>
<string name="menu_move" msgid="1828090633118079817">"Przenieś do…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nowe okno"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiuj"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Wklej"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Pokaż pamięć wewnętrzną"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopiuj"</string>
<string name="button_move" msgid="2202666023104202232">"Przenieś"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Zamknij"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Spróbuj ponownie"</string>
<string name="sort_name" msgid="9183560467917256779">"Według nazwy"</string>
<string name="sort_date" msgid="586080032956151448">"Według daty edycji"</string>
<string name="sort_size" msgid="3350681319735474741">"Według rozmiaru"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rBR/strings.xml b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
index ca984cffe69d..d72f0c3e585d 100644
--- a/packages/DocumentsUI/res/values-pt-rBR/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rBR/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Arquivos"</string>
<string name="title_open" msgid="4353228937663917801">"Abrir de"</string>
<string name="title_save" msgid="2433679664882857999">"Salvar em"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Criar pasta"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nova pasta"</string>
<string name="menu_grid" msgid="6878021334497835259">"Visualização em grade"</string>
<string name="menu_list" msgid="7279285939892417279">"Visualização em lista"</string>
<string name="menu_sort" msgid="7677740407158414452">"Classificar por"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Selecionar tudo"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copiar para..."</string>
<string name="menu_move" msgid="1828090633118079817">"Mover para..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nova janela"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiar"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Colar"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar armaz. interno"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
index ab673580ba19..43265a52c6f6 100644
--- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Ficheiros"</string>
<string name="title_open" msgid="4353228937663917801">"Abrir de"</string>
<string name="title_save" msgid="2433679664882857999">"Guardar em"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Criar pasta"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nova pasta"</string>
<string name="menu_grid" msgid="6878021334497835259">"Vista de grelha"</string>
<string name="menu_list" msgid="7279285939892417279">"Vista de lista"</string>
<string name="menu_sort" msgid="7677740407158414452">"Ordenar por"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Selecionar tudo"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copiar para…"</string>
<string name="menu_move" msgid="1828090633118079817">"Mover para..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nova janela"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiar"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Colar"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar mem. armaz. int."</string>
diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml
index ca984cffe69d..d72f0c3e585d 100644
--- a/packages/DocumentsUI/res/values-pt/strings.xml
+++ b/packages/DocumentsUI/res/values-pt/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Arquivos"</string>
<string name="title_open" msgid="4353228937663917801">"Abrir de"</string>
<string name="title_save" msgid="2433679664882857999">"Salvar em"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Criar pasta"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nova pasta"</string>
<string name="menu_grid" msgid="6878021334497835259">"Visualização em grade"</string>
<string name="menu_list" msgid="7279285939892417279">"Visualização em lista"</string>
<string name="menu_sort" msgid="7677740407158414452">"Classificar por"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Selecionar tudo"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copiar para..."</string>
<string name="menu_move" msgid="1828090633118079817">"Mover para..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nova janela"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiar"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Colar"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar armaz. interno"</string>
diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml
index e927b785e2f7..e880bbd5bb5c 100644
--- a/packages/DocumentsUI/res/values-ro/strings.xml
+++ b/packages/DocumentsUI/res/values-ro/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Fișiere"</string>
<string name="title_open" msgid="4353228937663917801">"Deschideți din"</string>
<string name="title_save" msgid="2433679664882857999">"Salvați în"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Creați un dosar"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Dosar nou"</string>
<string name="menu_grid" msgid="6878021334497835259">"Afișare tip grilă"</string>
<string name="menu_list" msgid="7279285939892417279">"Afișare tip listă"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sortați după"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Selectați tot"</string>
<string name="menu_copy" msgid="3612326052677229148">"Copiați în…"</string>
<string name="menu_move" msgid="1828090633118079817">"Mutați în…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Fereastră nouă"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Copiați"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Inserați"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Afișați stocarea internă"</string>
diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml
index cdf20cadfc61..e4755912a6c6 100644
--- a/packages/DocumentsUI/res/values-ru/strings.xml
+++ b/packages/DocumentsUI/res/values-ru/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Файлы"</string>
<string name="title_open" msgid="4353228937663917801">"Открыть"</string>
<string name="title_save" msgid="2433679664882857999">"Сохранить"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Новая папка"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Создать папку"</string>
<string name="menu_grid" msgid="6878021334497835259">"Сетка"</string>
<string name="menu_list" msgid="7279285939892417279">"Список"</string>
<string name="menu_sort" msgid="7677740407158414452">"Сортировать"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Выбрать все"</string>
<string name="menu_copy" msgid="3612326052677229148">"Копировать в…"</string>
<string name="menu_move" msgid="1828090633118079817">"Переместить"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Новое окно"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Копировать"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Вставить"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Внутренняя память"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Копировать"</string>
<string name="button_move" msgid="2202666023104202232">"Переместить"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Скрыть"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Повторить"</string>
<string name="sort_name" msgid="9183560467917256779">"По названию"</string>
<string name="sort_date" msgid="586080032956151448">"По дате изменения"</string>
<string name="sort_size" msgid="3350681319735474741">"По размеру"</string>
diff --git a/packages/DocumentsUI/res/values-si-rLK/strings.xml b/packages/DocumentsUI/res/values-si-rLK/strings.xml
index 138f8a6c6b76..e380b0a88383 100644
--- a/packages/DocumentsUI/res/values-si-rLK/strings.xml
+++ b/packages/DocumentsUI/res/values-si-rLK/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ගොනු"</string>
<string name="title_open" msgid="4353228937663917801">"විවෘත වන්නේ"</string>
<string name="title_save" msgid="2433679664882857999">"සුරකින්නේ"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"ෆෝල්ඩරයක් සාදන්න"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"නව ෆෝල්ඩරය"</string>
<string name="menu_grid" msgid="6878021334497835259">"ජාල පෙනුම"</string>
<string name="menu_list" msgid="7279285939892417279">"ලැයිස්තු පෙනුම"</string>
<string name="menu_sort" msgid="7677740407158414452">"අනුපිළිවෙලට සකසා ඇත්තේ"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"සියල්ල තෝරන්න"</string>
<string name="menu_copy" msgid="3612326052677229148">"වෙත පිටපත් කරන්න..."</string>
<string name="menu_move" msgid="1828090633118079817">"වෙත ගෙනයන්න..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"නව කවුළුව"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"පිටපත් කරන්න"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"අලවන්න"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"අභ්‍යන්තර ආචයනය පෙන්වන්න"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"පිටපත් කිරීම"</string>
<string name="button_move" msgid="2202666023104202232">"ගෙන යන්න"</string>
<string name="button_dismiss" msgid="3714065566893946085">"ඉවතලන්න"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"නැවත උත්සාහ කරන්න"</string>
<string name="sort_name" msgid="9183560467917256779">"නමින්"</string>
<string name="sort_date" msgid="586080032956151448">"වෙනස් කරන ලද දිනයෙන්"</string>
<string name="sort_size" msgid="3350681319735474741">"ප්‍රමාණය මගින්"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index 4310819f08a4..6614dcab68c0 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Súbory"</string>
<string name="title_open" msgid="4353228937663917801">"Otvoriť z"</string>
<string name="title_save" msgid="2433679664882857999">"Uložiť do"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Vytvoriť priečinok"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nový priečinok"</string>
<string name="menu_grid" msgid="6878021334497835259">"Zobrazenie mriežky"</string>
<string name="menu_list" msgid="7279285939892417279">"Zobrazenie zoznamu"</string>
<string name="menu_sort" msgid="7677740407158414452">"Zoradiť podľa"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Vybrať všetko"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopírovať do…"</string>
<string name="menu_move" msgid="1828090633118079817">"Presunúť do…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nové okno"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopírovať"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Prilepiť"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Zobraziť interné úložisko"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopírovať"</string>
<string name="button_move" msgid="2202666023104202232">"Presunúť"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Odmietnuť"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Skúsiť znova"</string>
<string name="sort_name" msgid="9183560467917256779">"Podľa názvu"</string>
<string name="sort_date" msgid="586080032956151448">"Podľa dátumu zmeny"</string>
<string name="sort_size" msgid="3350681319735474741">"Podľa veľkosti"</string>
diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml
index c9982a7a0233..58a4cc046081 100644
--- a/packages/DocumentsUI/res/values-sl/strings.xml
+++ b/packages/DocumentsUI/res/values-sl/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Datoteke"</string>
<string name="title_open" msgid="4353228937663917801">"Odpri iz mape"</string>
<string name="title_save" msgid="2433679664882857999">"Shrani v"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Ustvarjanje mape"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Nova mapa"</string>
<string name="menu_grid" msgid="6878021334497835259">"Mrežni pogled"</string>
<string name="menu_list" msgid="7279285939892417279">"Pogled seznama"</string>
<string name="menu_sort" msgid="7677740407158414452">"Razvrsti glede na"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Izberi vse"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopiraj v …"</string>
<string name="menu_move" msgid="1828090633118079817">"Premakni v ..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Novo okno"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiraj"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Prilepi"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Pokaži notranjo shrambo"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopiraj"</string>
<string name="button_move" msgid="2202666023104202232">"Premik"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Opusti"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Poskusite znova"</string>
<string name="sort_name" msgid="9183560467917256779">"Po imenu"</string>
<string name="sort_date" msgid="586080032956151448">"Po datumu spremembe"</string>
<string name="sort_size" msgid="3350681319735474741">"Po velikosti"</string>
diff --git a/packages/DocumentsUI/res/values-sq-rAL/strings.xml b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
index f1ee1bca33eb..7eba92f087e4 100644
--- a/packages/DocumentsUI/res/values-sq-rAL/strings.xml
+++ b/packages/DocumentsUI/res/values-sq-rAL/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Skedarët"</string>
<string name="title_open" msgid="4353228937663917801">"Hap nga"</string>
<string name="title_save" msgid="2433679664882857999">"Ruaje te"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Krijo dosje"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Dosje e re"</string>
<string name="menu_grid" msgid="6878021334497835259">"Pamje rrjete"</string>
<string name="menu_list" msgid="7279285939892417279">"Pamje liste"</string>
<string name="menu_sort" msgid="7677740407158414452">"Rendit sipas"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Zgjidhi të gjitha"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopjo te..."</string>
<string name="menu_move" msgid="1828090633118079817">"Zhvendos te..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Dritare e re"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopjo"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Ngjit"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Trego hapësirën e brendshme ruajtëse"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopjo"</string>
<string name="button_move" msgid="2202666023104202232">"Zhvendos"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Largoje"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Provo sërish"</string>
<string name="sort_name" msgid="9183560467917256779">"Sipas emrit"</string>
<string name="sort_date" msgid="586080032956151448">"Sipas datës së modifikimit"</string>
<string name="sort_size" msgid="3350681319735474741">"Sipas madhësisë"</string>
diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml
index c5116c2f78a9..0bce68772994 100644
--- a/packages/DocumentsUI/res/values-sr/strings.xml
+++ b/packages/DocumentsUI/res/values-sr/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Датотеке"</string>
<string name="title_open" msgid="4353228937663917801">"Отвори са"</string>
<string name="title_save" msgid="2433679664882857999">"Сачувај у"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Направи директоријум"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Нови директоријум"</string>
<string name="menu_grid" msgid="6878021334497835259">"Приказ мреже"</string>
<string name="menu_list" msgid="7279285939892417279">"Приказ листе"</string>
<string name="menu_sort" msgid="7677740407158414452">"Сортирај према"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Изабери све"</string>
<string name="menu_copy" msgid="3612326052677229148">"Копирај на..."</string>
<string name="menu_move" msgid="1828090633118079817">"Премести у..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Нови прозор"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Копирај"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Налепи"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Прикажи интерну меморију"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Копирај"</string>
<string name="button_move" msgid="2202666023104202232">"Премести"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Одбаци"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Покушај поново"</string>
<string name="sort_name" msgid="9183560467917256779">"Према имену"</string>
<string name="sort_date" msgid="586080032956151448">"Према датуму измене"</string>
<string name="sort_size" msgid="3350681319735474741">"Према величини"</string>
diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml
index 06e6514d8afb..a4a119c3fad5 100644
--- a/packages/DocumentsUI/res/values-sv/strings.xml
+++ b/packages/DocumentsUI/res/values-sv/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Filer"</string>
<string name="title_open" msgid="4353228937663917801">"Öppna från"</string>
<string name="title_save" msgid="2433679664882857999">"Spara till"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Skapa mapp"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Ny mapp"</string>
<string name="menu_grid" msgid="6878021334497835259">"Rutnätsvy"</string>
<string name="menu_list" msgid="7279285939892417279">"Listvy"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sortera efter"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Markera allt"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopiera till …"</string>
<string name="menu_move" msgid="1828090633118079817">"Flytta till ..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Nytt fönster"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopiera"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Klistra in"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Visa internminne"</string>
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index 79bae1f94cc8..91151fbe99e2 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Faili"</string>
<string name="title_open" msgid="4353228937663917801">"Fungua kutoka"</string>
<string name="title_save" msgid="2433679664882857999">"Hifadhi kwenye"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Unda folda"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Folda mpya"</string>
<string name="menu_grid" msgid="6878021334497835259">"Mwonekano gridi"</string>
<string name="menu_list" msgid="7279285939892417279">"Mwonekano orodha"</string>
<string name="menu_sort" msgid="7677740407158414452">"Panga kwa"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Chagua zote"</string>
<string name="menu_copy" msgid="3612326052677229148">"Nakili kwenda..."</string>
<string name="menu_move" msgid="1828090633118079817">"Hamisha hadi..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Dirisha jipya"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Nakili"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Bandika"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Onyesha hifadhi ya ndani"</string>
diff --git a/packages/DocumentsUI/res/values-sw720dp/styles.xml b/packages/DocumentsUI/res/values-sw720dp/styles.xml
index f4bc88ec463c..d415a849d33f 100644
--- a/packages/DocumentsUI/res/values-sw720dp/styles.xml
+++ b/packages/DocumentsUI/res/values-sw720dp/styles.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="DocumentsBaseTheme" parent="@*android:style/Theme.Material.DayNight.Dialog">
+ <style name="DocumentsBaseTheme" parent="@style/Theme.AppCompat.Dialog">
<!-- We do not specify width of window here because the max size of
floating window specified by windowFixedWidthis is limited. -->
<item name="*android:windowFixedHeightMajor">80%</item>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/strings.xml b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
index 117aabce296e..9a667b846b23 100644
--- a/packages/DocumentsUI/res/values-ta-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"கோப்புகள்"</string>
<string name="title_open" msgid="4353228937663917801">"இதில் திற"</string>
<string name="title_save" msgid="2433679664882857999">"இதில் சேமி"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"கோப்புறையை உருவாக்கு"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"புதிய கோப்புறை"</string>
<string name="menu_grid" msgid="6878021334497835259">"கட்டக் காட்சி"</string>
<string name="menu_list" msgid="7279285939892417279">"பட்டியல்"</string>
<string name="menu_sort" msgid="7677740407158414452">"இதன்படி வரிசைப்படுத்து"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"எல்லாவற்றையும் தேர்ந்தெடு"</string>
<string name="menu_copy" msgid="3612326052677229148">"இங்கு நகலெடு…"</string>
<string name="menu_move" msgid="1828090633118079817">"இதற்கு நகர்த்து…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"புதிய சாளரம்"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"நகலெடு"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"ஒட்டு"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"அகச் சேமிப்பகத்தைக் காட்டு"</string>
diff --git a/packages/DocumentsUI/res/values-te-rIN/strings.xml b/packages/DocumentsUI/res/values-te-rIN/strings.xml
index 21b7f586c3d6..91d436a2e43c 100644
--- a/packages/DocumentsUI/res/values-te-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-te-rIN/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ఫైల్‌లు"</string>
<string name="title_open" msgid="4353228937663917801">"ఇక్కడి నుండి తెరువు"</string>
<string name="title_save" msgid="2433679664882857999">"ఇందులో సేవ్ చేయి"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"ఫోల్డర్‌ను సృష్టించు"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"కొత్త ఫోల్డర్"</string>
<string name="menu_grid" msgid="6878021334497835259">"గ్రిడ్ వీక్షణ"</string>
<string name="menu_list" msgid="7279285939892417279">"జాబితా వీక్షణ"</string>
<string name="menu_sort" msgid="7677740407158414452">"ఇలా క్రమబద్ధీకరించు"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"అన్నీ ఎంచుకోండి"</string>
<string name="menu_copy" msgid="3612326052677229148">"ఇక్కడికి కాపీ చేయి…"</string>
<string name="menu_move" msgid="1828090633118079817">"దీనికి తరలించు..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"కొత్త విండో"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"కాపీ చేయి"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"అతికించు"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"అంతర్గత నిల్వను చూపు"</string>
diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml
index 8a49708ad591..9d77561d31bc 100644
--- a/packages/DocumentsUI/res/values-th/strings.xml
+++ b/packages/DocumentsUI/res/values-th/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"ไฟล์"</string>
<string name="title_open" msgid="4353228937663917801">"เปิดจาก"</string>
<string name="title_save" msgid="2433679664882857999">"บันทึกไปยัง"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"สร้างโฟลเดอร์"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"โฟลเดอร์ใหม่"</string>
<string name="menu_grid" msgid="6878021334497835259">"มุมมองตาราง"</string>
<string name="menu_list" msgid="7279285939892417279">"มุมมองรายการ"</string>
<string name="menu_sort" msgid="7677740407158414452">"จัดเรียงตาม"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"เลือกทั้งหมด"</string>
<string name="menu_copy" msgid="3612326052677229148">"คัดลอกไปยัง…"</string>
<string name="menu_move" msgid="1828090633118079817">"ย้ายไปที่…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"หน้าต่างใหม่"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"คัดลอก"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"วาง"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"แสดงที่จัดเก็บภายใน"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"คัดลอก"</string>
<string name="button_move" msgid="2202666023104202232">"ย้าย"</string>
<string name="button_dismiss" msgid="3714065566893946085">"ปิด"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"ลองอีกครั้ง"</string>
<string name="sort_name" msgid="9183560467917256779">"ตามชื่อ"</string>
<string name="sort_date" msgid="586080032956151448">"ตามวันที่ที่ปรับเปลี่ยน"</string>
<string name="sort_size" msgid="3350681319735474741">"ตามขนาด"</string>
diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml
index 5c0dc12e0353..9ae30aa91faf 100644
--- a/packages/DocumentsUI/res/values-tl/strings.xml
+++ b/packages/DocumentsUI/res/values-tl/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Mga File"</string>
<string name="title_open" msgid="4353228937663917801">"Buksan mula sa"</string>
<string name="title_save" msgid="2433679664882857999">"I-save sa"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Gumawa ng folder"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Bagong folder"</string>
<string name="menu_grid" msgid="6878021334497835259">"View na grid"</string>
<string name="menu_list" msgid="7279285939892417279">"View na listahan"</string>
<string name="menu_sort" msgid="7677740407158414452">"Uriin ayon sa"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Piliin lahat"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopyahin sa..."</string>
<string name="menu_move" msgid="1828090633118079817">"Ilipat sa…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Bagong window"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopyahin"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"I-paste"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ipakita internal storage"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopyahin"</string>
<string name="button_move" msgid="2202666023104202232">"Ilipat"</string>
<string name="button_dismiss" msgid="3714065566893946085">"I-dismiss"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Subukang Muli"</string>
<string name="sort_name" msgid="9183560467917256779">"Ayon sa pangalan"</string>
<string name="sort_date" msgid="586080032956151448">"Ayon sa petsa ng pagbago"</string>
<string name="sort_size" msgid="3350681319735474741">"Ayon sa laki"</string>
diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml
index 092b38ee7eef..3c2e39c9a4c7 100644
--- a/packages/DocumentsUI/res/values-tr/strings.xml
+++ b/packages/DocumentsUI/res/values-tr/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Dosyalar"</string>
<string name="title_open" msgid="4353228937663917801">"Şuradan aç:"</string>
<string name="title_save" msgid="2433679664882857999">"Şuraya kaydet:"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Klasör oluştur"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Yeni klasör"</string>
<string name="menu_grid" msgid="6878021334497835259">"Tablo görünümü"</string>
<string name="menu_list" msgid="7279285939892417279">"Liste görünümü"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sıralama ölçütü"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Tümünü seç"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopyala…"</string>
<string name="menu_move" msgid="1828090633118079817">"Taşı..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Yeni pencere"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopyala"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Yapıştır"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Dahili depolamayı göster"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Kopyala"</string>
<string name="button_move" msgid="2202666023104202232">"Taşı"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Kapat"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Tekrar Dene"</string>
<string name="sort_name" msgid="9183560467917256779">"Ada göre"</string>
<string name="sort_date" msgid="586080032956151448">"Değişiklik tarihine göre"</string>
<string name="sort_size" msgid="3350681319735474741">"Boyuta göre"</string>
diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml
index dff37c3eb316..407f1aa9ef8c 100644
--- a/packages/DocumentsUI/res/values-uk/strings.xml
+++ b/packages/DocumentsUI/res/values-uk/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Файли"</string>
<string name="title_open" msgid="4353228937663917801">"Відкрити"</string>
<string name="title_save" msgid="2433679664882857999">"Зберегти в"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Створити папку"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Нова папка"</string>
<string name="menu_grid" msgid="6878021334497835259">"Режим таблиці"</string>
<string name="menu_list" msgid="7279285939892417279">"Режим списку"</string>
<string name="menu_sort" msgid="7677740407158414452">"Параметри сортування"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Вибрати все"</string>
<string name="menu_copy" msgid="3612326052677229148">"Копіювати в…"</string>
<string name="menu_move" msgid="1828090633118079817">"Перемістити в…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Нове вікно"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Копіювати"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Вставити"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Показати внутр. пам’ять"</string>
diff --git a/packages/DocumentsUI/res/values-ur-rPK/strings.xml b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
index 82822ecdfe8d..845d2cb5df25 100644
--- a/packages/DocumentsUI/res/values-ur-rPK/strings.xml
+++ b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"فائلیں"</string>
<string name="title_open" msgid="4353228937663917801">"کھولیں از"</string>
<string name="title_save" msgid="2433679664882857999">"اس میں محفوظ کریں"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"فولڈر بنائیں"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"نیا فولڈر"</string>
<string name="menu_grid" msgid="6878021334497835259">"گرڈ منظر"</string>
<string name="menu_list" msgid="7279285939892417279">"فہرست منظر"</string>
<string name="menu_sort" msgid="7677740407158414452">"ترتیب دیں بلحاظ"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"سبھی کو منتخب کریں"</string>
<string name="menu_copy" msgid="3612326052677229148">"اس میں کاپی کریں…"</string>
<string name="menu_move" msgid="1828090633118079817">"اس میں منتقل کریں…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"نئی ونڈو"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"کاپی کریں"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"پیسٹ کریں"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"داخلی اسٹوریج دکھائیں"</string>
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
index d0cfacc8bc8c..f3514db7b288 100644
--- a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
+++ b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Fayllar"</string>
<string name="title_open" msgid="4353228937663917801">"Ochish"</string>
<string name="title_save" msgid="2433679664882857999">"Saqlash"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Jild yaratish"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Yangi jild"</string>
<string name="menu_grid" msgid="6878021334497835259">"Katak ko‘rinishida"</string>
<string name="menu_list" msgid="7279285939892417279">"Ro‘yxat ko‘rinishida"</string>
<string name="menu_sort" msgid="7677740407158414452">"Saralash"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Barchasini belgilash"</string>
<string name="menu_copy" msgid="3612326052677229148">"Nusxalash…"</string>
<string name="menu_move" msgid="1828090633118079817">"Ko‘chirib o‘tkazish…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Yangi oyna"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Nusxalash"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Joylash"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ichki xotirani ko‘rsatish"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Nusxalash"</string>
<string name="button_move" msgid="2202666023104202232">"Ko‘chirib o‘tkazish"</string>
<string name="button_dismiss" msgid="3714065566893946085">"O‘chirish"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Qayta urinish"</string>
<string name="sort_name" msgid="9183560467917256779">"Nomi bo‘yicha"</string>
<string name="sort_date" msgid="586080032956151448">"Tahrir sanasi bo‘yicha"</string>
<string name="sort_size" msgid="3350681319735474741">"Hajmi bo‘yicha"</string>
diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml
index 4583362fc6ab..52a4e821b55a 100644
--- a/packages/DocumentsUI/res/values-vi/strings.xml
+++ b/packages/DocumentsUI/res/values-vi/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Tệp"</string>
<string name="title_open" msgid="4353228937663917801">"Mở từ"</string>
<string name="title_save" msgid="2433679664882857999">"Lưu vào"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Tạo thư mục"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Thư mục mới"</string>
<string name="menu_grid" msgid="6878021334497835259">"Chế độ xem lưới"</string>
<string name="menu_list" msgid="7279285939892417279">"Chế độ xem danh sách"</string>
<string name="menu_sort" msgid="7677740407158414452">"Sắp xếp theo"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Chọn tất cả"</string>
<string name="menu_copy" msgid="3612326052677229148">"Sao chép vào…"</string>
<string name="menu_move" msgid="1828090633118079817">"Chuyển tới..."</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Cửa sổ mới"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Sao chép"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Dán"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Hiển thị bộ nhớ trong"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"Sao chép"</string>
<string name="button_move" msgid="2202666023104202232">"Di chuyển"</string>
<string name="button_dismiss" msgid="3714065566893946085">"Loại bỏ"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"Thử lại"</string>
<string name="sort_name" msgid="9183560467917256779">"Theo tên"</string>
<string name="sort_date" msgid="586080032956151448">"Theo ngày sửa đổi"</string>
<string name="sort_size" msgid="3350681319735474741">"Theo kích thước"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
index d577e14c8f40..8ee90cc1596e 100644
--- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"文件"</string>
<string name="title_open" msgid="4353228937663917801">"打开文件"</string>
<string name="title_save" msgid="2433679664882857999">"保存文件"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"新建文件夹"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"新建文件夹"</string>
<string name="menu_grid" msgid="6878021334497835259">"网格视图"</string>
<string name="menu_list" msgid="7279285939892417279">"列表视图"</string>
<string name="menu_sort" msgid="7677740407158414452">"排序依据"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"全选"</string>
<string name="menu_copy" msgid="3612326052677229148">"复制到…"</string>
<string name="menu_move" msgid="1828090633118079817">"移动到…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"新建窗口"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"复制"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"粘贴"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"显示内部存储设备"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"复制"</string>
<string name="button_move" msgid="2202666023104202232">"移动"</string>
<string name="button_dismiss" msgid="3714065566893946085">"关闭"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"重试"</string>
<string name="sort_name" msgid="9183560467917256779">"按名称"</string>
<string name="sort_date" msgid="586080032956151448">"按修改日期"</string>
<string name="sort_size" msgid="3350681319735474741">"按大小"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
index 3d44047126b9..dadc02983a40 100644
--- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"檔案"</string>
<string name="title_open" msgid="4353228937663917801">"開啟檔案"</string>
<string name="title_save" msgid="2433679664882857999">"儲存至"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"建立資料夾"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"新增資料夾"</string>
<string name="menu_grid" msgid="6878021334497835259">"格狀檢視"</string>
<string name="menu_list" msgid="7279285939892417279">"清單檢視"</string>
<string name="menu_sort" msgid="7677740407158414452">"排序方式"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"全部選取"</string>
<string name="menu_copy" msgid="3612326052677229148">"複製到…"</string>
<string name="menu_move" msgid="1828090633118079817">"移至…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"新視窗"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"複製"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"貼上"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"顯示內部儲存空間"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"複製"</string>
<string name="button_move" msgid="2202666023104202232">"移動"</string>
<string name="button_dismiss" msgid="3714065566893946085">"關閉"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"再試一次"</string>
<string name="sort_name" msgid="9183560467917256779">"按名稱"</string>
<string name="sort_date" msgid="586080032956151448">"按修改日期"</string>
<string name="sort_size" msgid="3350681319735474741">"按大小"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
index aaad98c89851..cdd1288cf027 100644
--- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"檔案"</string>
<string name="title_open" msgid="4353228937663917801">"開啟檔案"</string>
<string name="title_save" msgid="2433679664882857999">"儲存至"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"建立資料夾"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"新增資料夾"</string>
<string name="menu_grid" msgid="6878021334497835259">"格狀檢視"</string>
<string name="menu_list" msgid="7279285939892417279">"清單檢視"</string>
<string name="menu_sort" msgid="7677740407158414452">"排序依據"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"全選"</string>
<string name="menu_copy" msgid="3612326052677229148">"複製到…"</string>
<string name="menu_move" msgid="1828090633118079817">"移至…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"新視窗"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"複製"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"貼上"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"顯示內部儲存空間"</string>
@@ -45,8 +46,7 @@
<string name="button_copy" msgid="8706475544635021302">"複製"</string>
<string name="button_move" msgid="2202666023104202232">"移動"</string>
<string name="button_dismiss" msgid="3714065566893946085">"關閉"</string>
- <!-- no translation found for button_retry (4392027584153752797) -->
- <skip />
+ <string name="button_retry" msgid="4392027584153752797">"再試一次"</string>
<string name="sort_name" msgid="9183560467917256779">"依名稱"</string>
<string name="sort_date" msgid="586080032956151448">"依修改日期"</string>
<string name="sort_size" msgid="3350681319735474741">"依大小"</string>
diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml
index 8f20cc4db941..d6bb2b47ff9a 100644
--- a/packages/DocumentsUI/res/values-zu/strings.xml
+++ b/packages/DocumentsUI/res/values-zu/strings.xml
@@ -20,7 +20,7 @@
<string name="files_label" msgid="6051402950202690279">"Amafayela"</string>
<string name="title_open" msgid="4353228937663917801">"Vula kusuka ku-"</string>
<string name="title_save" msgid="2433679664882857999">"Londoloza ku-"</string>
- <string name="menu_create_dir" msgid="5947289605844398389">"Dala ifolda"</string>
+ <string name="menu_create_dir" msgid="2547620241173881754">"Ifolda entsha"</string>
<string name="menu_grid" msgid="6878021334497835259">"Ukubuka kwegridi"</string>
<string name="menu_list" msgid="7279285939892417279">"Ukubuka uhlu"</string>
<string name="menu_sort" msgid="7677740407158414452">"Hlunga nge-"</string>
@@ -33,6 +33,7 @@
<string name="menu_select_all" msgid="8323579667348729928">"Khetha konke"</string>
<string name="menu_copy" msgid="3612326052677229148">"Kopishela ku…"</string>
<string name="menu_move" msgid="1828090633118079817">"Hambisa ku…"</string>
+ <string name="menu_new_window" msgid="1226032889278727538">"Iwindi elisha"</string>
<string name="menu_copy_to_clipboard" msgid="489311381979634291">"Kopisha"</string>
<string name="menu_paste_from_clipboard" msgid="2071583031180257091">"Namathisela"</string>
<string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Bonisa isitoreji sangaphakathi"</string>
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index ce5b17432e8e..ed7820b01b48 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -15,5 +15,5 @@
-->
<resources>
- <bool name="productivity_device">false</bool>
+ <bool name="productivity_device">true</bool>
</resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index a4acb604c1ef..d21b5eec7d4f 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -27,7 +27,7 @@
<string name="title_save">Save to</string>
<!-- Menu item that creates a new directory/folder at the current location [CHAR LIMIT=24] -->
- <string name="menu_create_dir">Create folder</string>
+ <string name="menu_create_dir">New folder</string>
<!-- Menu item that switches view to show documents as a large-format grid of thumbnails [CHAR LIMIT=24] -->
<string name="menu_grid">Grid view</string>
<!-- Menu item that switches view to show documents as a list [CHAR LIMIT=24] -->
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index a6a45e59f469..9d2d171b05b6 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -20,6 +20,7 @@ import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+import static com.android.documentsui.Shared.DEBUG;
import static com.android.internal.util.Preconditions.checkArgument;
import android.app.Activity;
@@ -38,6 +39,7 @@ import android.provider.DocumentsContract.Root;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.util.Log;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -127,10 +129,10 @@ abstract class BaseActivity extends Activity {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- boolean shown = super.onPrepareOptionsMenu(menu);
+ super.onPrepareOptionsMenu(menu);
final RootInfo root = getCurrentRoot();
- final DocumentInfo cwd = getCurrentDirectory();
+ final boolean inRecents = getCurrentDirectory() == null;
final MenuItem sort = menu.findItem(R.id.menu_sort);
final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
@@ -140,26 +142,28 @@ abstract class BaseActivity extends Activity {
final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
final MenuItem settings = menu.findItem(R.id.menu_settings);
- mSearchManager.update(root);
+ // I'm thinkin' this isn't necesary here. If it is...'cuz of a bug....
+ // then uncomment the linke and let's get a proper bug reference here.
+ // mSearchManager.update(root);
// Search uses backend ranking; no sorting
- sort.setVisible(cwd != null && !mSearchManager.isSearching());
+ sort.setVisible(!inRecents && !mSearchManager.isSearching());
+
+ // grid/list is effectively a toggle.
+ grid.setVisible(mState.derivedMode != State.MODE_GRID);
+ list.setVisible(mState.derivedMode != State.MODE_LIST);
+
+ sortSize.setVisible(mState.showSize); // Only sort by size when visible
+ fileSize.setVisible(!mState.forceSize);
+ advanced.setVisible(!mState.forceAdvanced);
+ settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
? R.string.menu_advanced_hide : R.string.menu_advanced_show);
fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
? R.string.menu_file_size_hide : R.string.menu_file_size_show);
- State state = getDisplayState();
-
- sortSize.setVisible(state.showSize); // Only sort by size when visible
- fileSize.setVisible(!state.showSize);
- grid.setVisible(state.derivedMode != State.MODE_GRID);
- list.setVisible(state.derivedMode != State.MODE_LIST);
- advanced.setVisible(!mState.showAdvanced);
- settings.setVisible((root.flags & Root.FLAG_HAS_SETTINGS) != 0);
-
- return shown;
+ return true;
}
State buildDefaultState() {
@@ -185,12 +189,10 @@ abstract class BaseActivity extends Activity {
void onStackRestored(boolean restored, boolean external) {}
void onRootPicked(RootInfo root) {
- State state = getDisplayState();
-
// Clear entire backstack and start in new root
- state.stack.root = root;
- state.stack.clear();
- state.stackTouched = true;
+ mState.stack.root = root;
+ mState.stack.clear();
+ mState.stackTouched = true;
mSearchManager.update(root);
@@ -280,6 +282,7 @@ abstract class BaseActivity extends Activity {
return cwd != null
&& cwd.isCreateSupported()
&& !mSearchManager.isSearching()
+ && !root.isRecents()
&& !root.isDownloads();
}
@@ -289,8 +292,8 @@ abstract class BaseActivity extends Activity {
}
void openDirectory(DocumentInfo doc) {
- getDisplayState().stack.push(doc);
- getDisplayState().stackTouched = true;
+ mState.stack.push(doc);
+ mState.stackTouched = true;
onCurrentDirectoryChanged(ANIM_DOWN);
}
@@ -367,16 +370,15 @@ abstract class BaseActivity extends Activity {
}
void setDisplayAdvancedDevices(boolean display) {
- State state = getDisplayState();
LocalPreferences.setDisplayAdvancedDevices(this, display);
- state.showAdvanced = state.forceAdvanced | display;
+ mState.showAdvanced = mState.forceAdvanced | display;
RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
invalidateOptionsMenu();
}
void setDisplayFileSize(boolean display) {
LocalPreferences.setDisplayFileSize(this, display);
- getDisplayState().showSize = display;
+ mState.showSize = display;
DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
invalidateOptionsMenu();
}
@@ -389,7 +391,7 @@ abstract class BaseActivity extends Activity {
* Set state sort order based on explicit user action.
*/
void setUserSortOrder(int sortOrder) {
- getDisplayState().userSortOrder = sortOrder;
+ mState.userSortOrder = sortOrder;
DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
}
@@ -397,7 +399,7 @@ abstract class BaseActivity extends Activity {
* Set state mode based on explicit user action.
*/
void setUserMode(int mode) {
- getDisplayState().userMode = mode;
+ mState.userMode = mode;
DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
}
@@ -411,7 +413,7 @@ abstract class BaseActivity extends Activity {
@Override
protected void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
- state.putParcelable(EXTRA_STATE, getDisplayState());
+ state.putParcelable(EXTRA_STATE, mState);
}
@Override
@@ -420,16 +422,15 @@ abstract class BaseActivity extends Activity {
}
RootInfo getCurrentRoot() {
- State state = getDisplayState();
- if (state.stack.root != null) {
- return state.stack.root;
+ if (mState.stack.root != null) {
+ return mState.stack.root;
} else {
return mRoots.getRecentsRoot();
}
}
public DocumentInfo getCurrentDirectory() {
- return getDisplayState().stack.peek();
+ return mState.stack.peek();
}
public Executor getExecutorForCurrentDirectory() {
@@ -465,14 +466,28 @@ abstract class BaseActivity extends Activity {
}
}
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (DEBUG) Log.d(mTag, "onKeyUp: keycode = " + keyCode);
+ DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_MOVE_HOME:
+ dir.focusFirstFile();
+ return true;
+ case KeyEvent.KEYCODE_MOVE_END:
+ dir.focusLastFile();
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
public void onStackPicked(DocumentStack stack) {
try {
// Update the restored stack to ensure we have freshest data
stack.updateDocuments(getContentResolver());
- State state = getDisplayState();
- state.stack = stack;
- state.stackTouched = true;
+ mState.stack = stack;
+ mState.stackTouched = true;
onCurrentDirectoryChanged(ANIM_SIDE);
} catch (FileNotFoundException e) {
@@ -502,9 +517,8 @@ abstract class BaseActivity extends Activity {
@Override
protected void onPostExecute(DocumentInfo result) {
if (result != null) {
- State state = getDisplayState();
- state.stack.push(result);
- state.stackTouched = true;
+ mState.stack.push(result);
+ mState.stackTouched = true;
onCurrentDirectoryChanged(ANIM_SIDE);
}
}
@@ -516,7 +530,9 @@ abstract class BaseActivity extends Activity {
@Override
protected Void doInBackground(Void... params) {
- State state = getDisplayState();
+ if (DEBUG && !mState.stack.isEmpty()) {
+ Log.w(mTag, "Overwriting existing stack.");
+ }
RootsCache roots = DocumentsApplication.getRootsCache(BaseActivity.this);
// Restore last stack for calling package
@@ -528,7 +544,7 @@ abstract class BaseActivity extends Activity {
mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
final byte[] rawStack = cursor.getBlob(
cursor.getColumnIndex(ResumeColumns.STACK));
- DurableUtils.readFromArray(rawStack, state.stack);
+ DurableUtils.readFromArray(rawStack, mState.stack);
mRestoredStack = true;
}
} catch (IOException e) {
@@ -539,13 +555,13 @@ abstract class BaseActivity extends Activity {
if (mRestoredStack) {
// Update the restored stack to ensure we have freshest data
- final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(state);
+ final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState);
try {
- state.stack.updateRoot(matchingRoots);
- state.stack.updateDocuments(getContentResolver());
+ mState.stack.updateRoot(matchingRoots);
+ mState.stack.updateDocuments(getContentResolver());
} catch (FileNotFoundException e) {
Log.w(mTag, "Failed to restore stack: " + e);
- state.stack.reset();
+ mState.stack.reset();
mRestoredStack = false;
}
}
@@ -556,7 +572,7 @@ abstract class BaseActivity extends Activity {
@Override
protected void onPostExecute(Void result) {
if (isDestroyed()) return;
- getDisplayState().restored = true;
+ mState.restored = true;
onCurrentDirectoryChanged(ANIM_NONE);
onStackRestored(mRestoredStack, mExternal);
}
@@ -600,10 +616,9 @@ abstract class BaseActivity extends Activity {
return;
}
- State state = getDisplayState();
- while (state.stack.size() > position + 1) {
- state.stackTouched = true;
- state.stack.pop();
+ while (mState.stack.size() > position + 1) {
+ mState.stackTouched = true;
+ mState.stack.pop();
}
onCurrentDirectoryChanged(ANIM_UP);
}
@@ -620,13 +635,12 @@ abstract class BaseActivity extends Activity {
final class StackAdapter extends BaseAdapter {
@Override
public int getCount() {
- return getDisplayState().stack.size();
+ return mState.stack.size();
}
@Override
public DocumentInfo getItem(int position) {
- State state = getDisplayState();
- return state.stack.get(state.stack.size() - position - 1);
+ return mState.stack.get(mState.stack.size() - position - 1);
}
@Override
@@ -714,13 +728,12 @@ abstract class BaseActivity extends Activity {
return;
}
- State state = getDisplayState();
- if (state.currentSearch != null) {
+ if (mState.currentSearch != null) {
mMenu.expandActionView();
mView.setIconified(false);
mView.clearFocus();
- mView.setQuery(state.currentSearch, false);
+ mView.setQuery(mState.currentSearch, false);
} else {
mView.clearFocus();
if (!mView.isIconified()) {
@@ -746,7 +759,7 @@ abstract class BaseActivity extends Activity {
mMenu.setVisible(visible);
if (!visible) {
- getDisplayState().currentSearch = null;
+ mState.currentSearch = null;
}
}
@@ -764,7 +777,7 @@ abstract class BaseActivity extends Activity {
}
boolean isSearching() {
- return getDisplayState().currentSearch != null;
+ return mState.currentSearch != null;
}
boolean isExpanded() {
@@ -779,7 +792,7 @@ abstract class BaseActivity extends Activity {
return false;
}
- getDisplayState().currentSearch = null;
+ mState.currentSearch = null;
onCurrentDirectoryChanged(ANIM_NONE);
return false;
}
@@ -798,7 +811,7 @@ abstract class BaseActivity extends Activity {
mIgnoreNextCollapse = false;
return true;
}
- getDisplayState().currentSearch = null;
+ mState.currentSearch = null;
onCurrentDirectoryChanged(ANIM_NONE);
return true;
}
@@ -806,7 +819,7 @@ abstract class BaseActivity extends Activity {
@Override
public boolean onQueryTextSubmit(String query) {
mSearchExpanded = true;
- getDisplayState().currentSearch = query;
+ mState.currentSearch = query;
mView.clearFocus();
onCurrentDirectoryChanged(ANIM_NONE);
return true;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
index 815fbfe8e684..047949f54ad8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CopyService.java
@@ -34,6 +34,7 @@ import android.net.Uri;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.DocumentsContract;
@@ -62,7 +63,6 @@ public class CopyService extends IntentService {
private static final String EXTRA_CANCEL = "com.android.documentsui.CANCEL";
public static final String EXTRA_SRC_LIST = "com.android.documentsui.SRC_LIST";
- public static final String EXTRA_STACK = "com.android.documentsui.STACK";
public static final String EXTRA_FAILURE = "com.android.documentsui.FAILURE";
public static final String EXTRA_TRANSFER_MODE = "com.android.documentsui.TRANSFER_MODE";
@@ -73,6 +73,8 @@ public class CopyService extends IntentService {
// TODO: Move it to a shared file when more operations are implemented.
public static final int FAILURE_COPY = 1;
+ private PowerManager mPowerManager;
+
private NotificationManager mNotificationManager;
private Notification.Builder mProgressBuilder;
@@ -115,12 +117,12 @@ public class CopyService extends IntentService {
final Intent copyIntent = new Intent(activity, CopyService.class);
copyIntent.putParcelableArrayListExtra(
EXTRA_SRC_LIST, new ArrayList<DocumentInfo>(srcDocs));
- copyIntent.putExtra(EXTRA_STACK, (Parcelable) dstStack);
+ copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) dstStack);
copyIntent.putExtra(EXTRA_TRANSFER_MODE, mode);
int toastMessage = (mode == TRANSFER_MODE_COPY) ? R.plurals.copy_begin
: R.plurals.move_begin;
- Shared.makeSnackbar(activity,
+ Snackbars.makeSnackbar(activity,
res.getQuantityString(toastMessage, srcDocs.size(), srcDocs.size()),
Snackbar.LENGTH_SHORT).show();
activity.startService(copyIntent);
@@ -141,12 +143,16 @@ public class CopyService extends IntentService {
return;
}
+ final PowerManager.WakeLock wakeLock = mPowerManager
+ .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
final ArrayList<DocumentInfo> srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST);
- final DocumentStack stack = intent.getParcelableExtra(EXTRA_STACK);
+ final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
// Copy by default.
final int transferMode = intent.getIntExtra(EXTRA_TRANSFER_MODE, TRANSFER_MODE_COPY);
try {
+ wakeLock.acquire();
+
// Acquire content providers.
mSrcClient = DocumentsApplication.acquireUnstableProviderOrThrow(getContentResolver(),
srcs.get(0).authority);
@@ -166,6 +172,8 @@ public class CopyService extends IntentService {
ContentProviderClient.releaseQuietly(mSrcClient);
ContentProviderClient.releaseQuietly(mDstClient);
+ wakeLock.release();
+
// Dismiss the ongoing copy notification when the copy is done.
mNotificationManager.cancel(mJobId, 0);
@@ -173,7 +181,7 @@ public class CopyService extends IntentService {
Log.e(TAG, mFailedFiles.size() + " files failed to copy");
final Context context = getApplicationContext();
final Intent navigateIntent = new Intent(context, FilesActivity.class);
- navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack);
+ navigateIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
navigateIntent.putExtra(EXTRA_FAILURE, FAILURE_COPY);
navigateIntent.putExtra(EXTRA_TRANSFER_MODE, transferMode);
navigateIntent.putParcelableArrayListExtra(EXTRA_SRC_LIST, mFailedFiles);
@@ -199,7 +207,8 @@ public class CopyService extends IntentService {
@Override
public void onCreate() {
super.onCreate();
- mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ mPowerManager = getSystemService(PowerManager.class);
+ mNotificationManager = getSystemService(NotificationManager.class);
}
/**
@@ -221,7 +230,7 @@ public class CopyService extends IntentService {
final Context context = getApplicationContext();
final Intent navigateIntent = new Intent(context, FilesActivity.class);
- navigateIntent.putExtra(EXTRA_STACK, (Parcelable) stack);
+ navigateIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
final String contentTitle = getString(copying ? R.string.copy_notification_title
: R.string.move_notification_title);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index 9f4451663706..c6425a6e41f2 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -147,7 +147,7 @@ public class CreateDirectoryFragment extends DialogFragment {
// Navigate into newly created child
mActivity.onDirectoryCreated(result);
} else {
- Shared.makeSnackbar(mActivity, R.string.create_error, Snackbar.LENGTH_SHORT).show();
+ Snackbars.makeSnackbar(mActivity, R.string.create_error, Snackbar.LENGTH_SHORT).show();
}
mActivity.setPending(false);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 3c6be6e47b11..2fe829f266ba 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -44,7 +44,6 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
-import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -54,7 +53,6 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.CancellationSignal;
-import android.os.Handler;
import android.os.Looper;
import android.os.OperationCanceledException;
import android.os.Parcelable;
@@ -134,8 +132,6 @@ public class DirectoryFragment extends Fragment {
private Model mModel;
private Model.UpdateListener mModelUpdateListener = new ModelUpdateListener();
- private final Handler mHandler = new Handler(Looper.getMainLooper());
-
private View mEmptyView;
private RecyclerView mRecView;
@@ -217,8 +213,6 @@ public class DirectoryFragment extends Fragment {
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- final Context context = inflater.getContext();
- final Resources res = context.getResources();
final View view = inflater.inflate(R.layout.fragment_directory, container, false);
mMessageBar = MessageBar.create(getChildFragmentManager());
@@ -423,7 +417,7 @@ public class DirectoryFragment extends Fragment {
}
CopyService.start(getActivity(), getDisplayState(this).selectedDocumentsForCopy,
- (DocumentStack) data.getParcelableExtra(CopyService.EXTRA_STACK),
+ (DocumentStack) data.getParcelableExtra(Shared.EXTRA_STACK),
data.getIntExtra(CopyService.EXTRA_TRANSFER_MODE, CopyService.TRANSFER_MODE_NONE));
}
@@ -678,6 +672,7 @@ public class DirectoryFragment extends Fragment {
checkNotNull(mMenu);
// Delegate update logic to our owning action, since specialized logic is desired.
mFragmentTuner.updateActionMenu(mMenu, mType, mNoDeleteCount == 0);
+ Menus.disableHiddenItems(mMenu);
}
@Override
@@ -799,13 +794,12 @@ public class DirectoryFragment extends Fragment {
private void deleteDocuments(final Selection selected) {
Context context = getActivity();
- ContentResolver resolver = context.getContentResolver();
String message = Shared.getQuantityString(context, R.plurals.deleting, selected.size());
mModel.markForDeletion(selected);
final Activity activity = getActivity();
- Shared.makeSnackbar(activity, message, Snackbar.LENGTH_LONG)
+ Snackbars.makeSnackbar(activity, message, Snackbar.LENGTH_LONG)
.setAction(
R.string.undo,
new android.view.View.OnClickListener() {
@@ -823,7 +817,7 @@ public class DirectoryFragment extends Fragment {
new Model.DeletionListener() {
@Override
public void onError() {
- Shared.makeSnackbar(
+ Snackbars.makeSnackbar(
activity,
R.string.toast_failed_delete,
Snackbar.LENGTH_LONG)
@@ -879,6 +873,10 @@ public class DirectoryFragment extends Fragment {
public DocumentHolder(View view) {
super(view);
this.view = view;
+ // Setting this using android:focusable in the item layouts doesn't work for list items.
+ // So we set it here. Note that touch mode focus is a separate issue - see
+ // View.setFocusableInTouchMode and View.isInTouchMode for more info.
+ this.view.setFocusable(true);
}
}
@@ -1244,7 +1242,7 @@ public class DirectoryFragment extends Fragment {
private void copyDocuments(final List<DocumentInfo> docs, final DocumentInfo destination) {
if (!canCopy(docs, destination)) {
- Shared.makeSnackbar(
+ Snackbars.makeSnackbar(
getActivity(),
R.string.clipboard_files_cannot_paste,
Snackbar.LENGTH_SHORT)
@@ -1298,7 +1296,7 @@ public class DirectoryFragment extends Fragment {
void onDocumentsReady(List<DocumentInfo> docs) {
mClipper.clipDocuments(docs);
Activity activity = getActivity();
- Shared.makeSnackbar(activity,
+ Snackbars.makeSnackbar(activity,
activity.getResources().getQuantityString(
R.plurals.clipboard_files_clipped, docs.size(), docs.size()),
Snackbar.LENGTH_SHORT).show();
@@ -1343,6 +1341,45 @@ public class DirectoryFragment extends Fragment {
}
}
+ /**
+ * Scrolls to the top of the file list and focuses the first file.
+ */
+ void focusFirstFile() {
+ focusFile(0);
+ }
+
+ /**
+ * Scrolls to the bottom of the file list and focuses the last file.
+ */
+ void focusLastFile() {
+ focusFile(mAdapter.getItemCount() - 1);
+ }
+
+ /**
+ * Scrolls to and then focuses on the file at the given position.
+ */
+ private void focusFile(final int pos) {
+ // Don't smooth scroll; that taxes the system unnecessarily and makes the scroll handling
+ // logic below more complicated.
+ mRecView.scrollToPosition(pos);
+
+ // If the item is already in view, focus it; otherwise, set a one-time listener to focus it
+ // when the scroll is completed.
+ RecyclerView.ViewHolder vh = mRecView.findViewHolderForAdapterPosition(pos);
+ if (vh != null) {
+ vh.itemView.requestFocus();
+ } else {
+ mRecView.addOnScrollListener(
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(RecyclerView view, int dx, int dy) {
+ view.findViewHolderForAdapterPosition(pos).itemView.requestFocus();
+ view.removeOnScrollListener(this);
+ }
+ });
+ }
+ }
+
private void setupDragAndDropOnDirectoryView(View view) {
// Listen for drops on non-directory items and empty space.
view.setOnDragListener(mOnDragListener);
@@ -1608,23 +1645,25 @@ public class DirectoryFragment extends Fragment {
@Override
public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+ boolean copyEnabled = mManaging && dirType != TYPE_RECENT_OPEN;
+ // TODO: The selection needs to be deletable.
+ boolean moveEnabled =
+ SystemProperties.getBoolean("debug.documentsui.enable_move", false);
+ menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(copyEnabled);
final MenuItem open = menu.findItem(R.id.menu_open);
final MenuItem share = menu.findItem(R.id.menu_share);
final MenuItem delete = menu.findItem(R.id.menu_delete);
final MenuItem copyTo = menu.findItem(R.id.menu_copy_to);
final MenuItem moveTo = menu.findItem(R.id.menu_move_to);
- final MenuItem copyToClipboard = menu.findItem(R.id.menu_copy_to_clipboard);
open.setVisible(!mManaging);
share.setVisible(mManaging);
delete.setVisible(mManaging && canDelete);
- // Disable copying from the Recents view.
- copyTo.setVisible(mManaging && dirType != TYPE_RECENT_OPEN);
- moveTo.setVisible(SystemProperties.getBoolean("debug.documentsui.enable_move", false));
-
- // Only shown in files mode.
- copyToClipboard.setVisible(false);
+ copyTo.setVisible(copyEnabled);
+ copyTo.setEnabled(copyEnabled);
+ moveTo.setVisible(moveEnabled);
+ moveTo.setEnabled(moveEnabled);
}
@Override
@@ -1638,13 +1677,14 @@ public class DirectoryFragment extends Fragment {
@Override
public void updateActionMenu(Menu menu, int dirType, boolean canDelete) {
+ menu.findItem(R.id.menu_copy_to_clipboard).setEnabled(dirType != TYPE_RECENT_OPEN);
+
menu.findItem(R.id.menu_share).setVisible(true);
menu.findItem(R.id.menu_delete).setVisible(canDelete);
- menu.findItem(R.id.menu_copy_to_clipboard).setVisible(true);
menu.findItem(R.id.menu_open).setVisible(false);
- menu.findItem(R.id.menu_copy_to).setVisible(false);
- menu.findItem(R.id.menu_move_to).setVisible(false);
+ menu.findItem(R.id.menu_copy_to).setVisible(true);
+ menu.findItem(R.id.menu_move_to).setVisible(true);
}
@Override
@@ -1756,7 +1796,7 @@ public class DirectoryFragment extends Fragment {
}
}
- if (DEBUG) {
+ if (DEBUG && position != originalPos) {
Log.d(TAG, "Item position adjusted for deletion. Original: " + originalPos
+ " Adjusted: " + position);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index f6ded4b7df5a..6b428f58441c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -89,8 +89,6 @@ public class DocumentsActivity extends BaseActivity {
setTheme(R.style.DocumentsNonDialogTheme);
}
- final Context context = this;
-
if (mShowAsDialog) {
mDrawer = DrawerController.createDummy();
@@ -314,42 +312,36 @@ public class DocumentsActivity extends BaseActivity {
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
final MenuItem grid = menu.findItem(R.id.menu_grid);
final MenuItem list = menu.findItem(R.id.menu_list);
- final MenuItem advanced = menu.findItem(R.id.menu_advanced);
final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
final MenuItem settings = menu.findItem(R.id.menu_settings);
- boolean fileSizeVisible = mState.showSize && !mState.forceSize;
- if (mState.action == ACTION_CREATE
+ boolean recents = cwd == null;
+ boolean picking = mState.action == ACTION_CREATE
|| mState.action == ACTION_OPEN_TREE
- || mState.action == ACTION_OPEN_COPY_DESTINATION) {
- createDir.setVisible(cwd != null && cwd.isCreateSupported());
- mSearchManager.showMenu(false);
-
- // No display options in recent directories
- if (cwd == null) {
- grid.setVisible(false);
- list.setVisible(false);
- fileSizeVisible = false;
- }
+ || mState.action == ACTION_OPEN_COPY_DESTINATION;
- if (mState.action == ACTION_CREATE) {
- final FragmentManager fm = getFragmentManager();
- SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
- }
- } else {
- createDir.setVisible(false);
- }
+ createDir.setVisible(picking && !recents && cwd.isCreateSupported());
+ mSearchManager.showMenu(!picking);
- advanced.setVisible(!mState.forceAdvanced);
- fileSize.setVisible(fileSizeVisible);
+ // No display options in recent directories
+ grid.setVisible(!(picking && recents));
+ list.setVisible(!(picking && recents));
+
+ fileSize.setVisible(fileSize.isVisible() && !picking);
settings.setVisible(false);
+ if (mState.action == ACTION_CREATE) {
+ final FragmentManager fm = getFragmentManager();
+ SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
+ }
+
+ Menus.disableHiddenItems(menu);
+
return true;
}
@@ -510,7 +502,7 @@ public class DocumentsActivity extends BaseActivity {
} else if (mState.action == ACTION_OPEN_COPY_DESTINATION) {
// Picking a copy destination is only used internally by us, so we
// don't need to extend permissions to the caller.
- intent.putExtra(CopyService.EXTRA_STACK, (Parcelable) mState.stack);
+ intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
intent.putExtra(CopyService.EXTRA_TRANSFER_MODE, mState.transferMode);
} else {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -611,7 +603,7 @@ public class DocumentsActivity extends BaseActivity {
if (result != null) {
onTaskFinished(result);
} else {
- Shared.makeSnackbar(
+ Snackbars.makeSnackbar(
DocumentsActivity.this, R.string.save_error, Snackbar.LENGTH_SHORT).show();
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
index ea0c18acf26f..120f6106a7a0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FailureDialogFragment.java
@@ -66,7 +66,7 @@ public class FailureDialogFragment extends DialogFragment
if (whichButton == DialogInterface.BUTTON_POSITIVE) {
CopyService.start(getActivity(), mFailedSrcList,
(DocumentStack) getActivity().getIntent().getParcelableExtra(
- CopyService.EXTRA_STACK),
+ Shared.EXTRA_STACK),
mTransferMode);
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index df803fbc4bb2..1330b3cbc7cd 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -30,6 +30,8 @@ import android.content.ContentValues;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Parcelable;
+import android.provider.DocumentsContract;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.util.Log;
@@ -85,32 +87,51 @@ public class FilesActivity extends BaseActivity {
mDrawer = DrawerController.create(this);
RootsFragment.show(getFragmentManager(), null);
- if (!mState.restored) {
- Intent intent = getIntent();
- Uri rootUri = intent.getData();
- // If we've got a specific root to display, restore that root using a dedicated
- // authority. That way a misbehaving provider won't result in an ANR.
- if (rootUri != null && !LauncherActivity.isLaunchUri(rootUri)) {
- new RestoreRootTask(rootUri).executeOnExecutor(
- ProviderExecutor.forAuthority(rootUri.getAuthority()));
+ if (mState.restored) {
+ if (DEBUG) Log.d(TAG, "Restored instance for uri: " + getIntent().getData());
+ onCurrentDirectoryChanged(ANIM_NONE);
+ } else {
+ Intent intent = getIntent();
+ Uri uri = intent.getData();
+
+ if (DEBUG) Log.d(TAG, "Creating new instance for uri: " + uri);
+ // If a non-empty stack is present in our state it was read (presumably)
+ // from EXTRA_STACK intent extra. In this case, we'll skip other means of
+ // loading or restoring the stack.
+ if (!mState.stack.isEmpty()) {
+ // When restoring from a stack, if a URI is present, it should only ever
+ // be a launch URI. Launch URIs support sensible activity management, but
+ // don't specify an real content target.
+ if (uri != null) {
+ checkState(LauncherActivity.isLaunchUri(uri));
+ }
+ onCurrentDirectoryChanged(ANIM_NONE);
+ } else if (DocumentsContract.isRootUri(this, uri)) {
+ // If we've got a specific root to display, restore that root using a dedicated
+ // authority. That way a misbehaving provider won't result in an ANR.
+ new RestoreRootTask(uri).executeOnExecutor(
+ ProviderExecutor.forAuthority(uri.getAuthority()));
} else {
+ // Finally, we try to restore a stack from recents.
new RestoreStackTask().execute();
}
+ // TODO: Ensure we're handling CopyService errors correctly across all activities.
// Show a failure dialog if there was a failed operation.
- final DocumentStack dstStack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
final int failure = intent.getIntExtra(CopyService.EXTRA_FAILURE, 0);
final int transferMode = intent.getIntExtra(CopyService.EXTRA_TRANSFER_MODE,
CopyService.TRANSFER_MODE_NONE);
if (failure != 0) {
final ArrayList<DocumentInfo> failedSrcList =
intent.getParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST);
- FailureDialogFragment.show(getFragmentManager(), failure, failedSrcList, dstStack,
+ FailureDialogFragment.show(
+ getFragmentManager(),
+ failure,
+ failedSrcList,
+ mState.stack,
transferMode);
}
- } else {
- onCurrentDirectoryChanged(ANIM_NONE);
}
}
@@ -126,7 +147,7 @@ public class FilesActivity extends BaseActivity {
// Options specific to the DocumentsActivity.
checkArgument(!intent.hasExtra(Intent.EXTRA_LOCAL_ONLY));
- final DocumentStack stack = intent.getParcelableExtra(CopyService.EXTRA_STACK);
+ final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
if (stack != null) {
state.stack = stack;
}
@@ -206,30 +227,23 @@ public class FilesActivity extends BaseActivity {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- boolean shown = super.onPrepareOptionsMenu(menu);
-
- menu.findItem(R.id.menu_file_size).setVisible(true);
- menu.findItem(R.id.menu_advanced).setVisible(true);
+ super.onPrepareOptionsMenu(menu);
final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
final MenuItem newWindow = menu.findItem(R.id.menu_new_window);
final MenuItem pasteFromCb = menu.findItem(R.id.menu_paste_from_clipboard);
- boolean canCreateDir = canCreateDirectory();
-
createDir.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
- createDir.setVisible(canCreateDir);
- createDir.setEnabled(canCreateDir);
+ createDir.setVisible(true);
+ createDir.setEnabled(canCreateDirectory());
+
+ pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
newWindow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
newWindow.setVisible(mProductivityDevice);
- newWindow.setEnabled(mProductivityDevice);
- pasteFromCb.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
- pasteFromCb.setVisible(true);
- pasteFromCb.setEnabled(mClipper.hasItemsToPaste());
-
- return shown;
+ Menus.disableHiddenItems(menu, pasteFromCb);
+ return true;
}
@Override
@@ -240,7 +254,7 @@ public class FilesActivity extends BaseActivity {
showCreateDirectoryDialog();
return true;
case R.id.menu_new_window:
- startActivity(LauncherActivity.createLaunchIntent(this));
+ createNewWindow();
return true;
case R.id.menu_paste_from_clipboard:
DirectoryFragment dir = DirectoryFragment.get(getFragmentManager());
@@ -252,6 +266,12 @@ public class FilesActivity extends BaseActivity {
return super.onOptionsItemSelected(item);
}
+ private void createNewWindow() {
+ Intent intent = LauncherActivity.createLaunchIntent(this);
+ intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
+ startActivity(intent);
+ }
+
@Override
void onDirectoryChanged(int anim) {
final FragmentManager fm = getFragmentManager();
@@ -322,7 +342,7 @@ public class FilesActivity extends BaseActivity {
try {
startActivity(intent);
} catch (ActivityNotFoundException ex2) {
- Shared.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
+ Snackbars.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
index 17a1161f7c25..7426af56ecb1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilteringCursorWrapper.java
@@ -16,6 +16,7 @@
package com.android.documentsui;
+import static com.android.documentsui.Shared.DEBUG;
import static com.android.documentsui.Shared.TAG;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
@@ -65,7 +66,9 @@ public class FilteringCursorWrapper extends AbstractCursor {
}
}
- Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount);
+ if (DEBUG && mCount != cursor.getCount()) {
+ Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount);
+ }
}
@Override
diff --git a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
index c29937d68cdf..b3d0cf37bbd5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
@@ -16,15 +16,17 @@
package com.android.documentsui;
+import static com.android.documentsui.Shared.DEBUG;
+
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.AppTask;
-import android.app.ActivityManager.RecentTaskInfo;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.util.Log;
import java.util.List;
@@ -39,39 +41,47 @@ import java.util.List;
*/
public class LauncherActivity extends Activity {
- public static final String LAUNCH_CONTROL_AUTHORITY = "com.android.documentsui.launchControl";
+ private static final String LAUNCH_CONTROL_AUTHORITY = "com.android.documentsui.launchControl";
+ private static final String TAG = "LauncherActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityManager activities = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
- List<AppTask> tasks = activities.getAppTasks();
- AppTask raiseTask = null;
- for (AppTask task : tasks) {
- Uri taskUri = task.getTaskInfo().baseIntent.getData();
- if (taskUri != null && isLaunchUri(taskUri)) {
- raiseTask = task;
- }
- }
-
- if (raiseTask == null) {
- launchFilesTask();
+ Intent intent = findTask(activities);
+ if (intent != null) {
+ restoreTask(intent);
} else {
- raiseFilesTask(activities, raiseTask.getTaskInfo());
+ startTask();
}
finish();
}
- private void launchFilesTask() {
+ private @Nullable Intent findTask(ActivityManager activities) {
+ List<AppTask> tasks = activities.getAppTasks();
+ for (AppTask task : tasks) {
+ Intent intent = task.getTaskInfo().baseIntent;
+ Uri uri = intent.getData();
+ if (isLaunchUri(uri)) {
+ return intent;
+ }
+ }
+ return null;
+ }
+
+ private void startTask() {
Intent intent = createLaunchIntent(this);
+ if (DEBUG) Log.d(TAG, "Starting new task > " + intent.getData());
startActivity(intent);
}
- private void raiseFilesTask(ActivityManager activities, RecentTaskInfo task) {
- activities.moveTaskToFront(task.id, 0);
+ private void restoreTask(Intent intent) {
+ if (DEBUG) Log.d(TAG, "Restoring existing task > " + intent.getData());
+ // TODO: This doesn't appear to restore a task once it has stopped running.
+ startActivity(intent);
}
static Intent createLaunchIntent(Context context) {
@@ -88,6 +98,7 @@ public class LauncherActivity extends Activity {
}
static boolean isLaunchUri(@Nullable Uri uri) {
- return uri != null && LAUNCH_CONTROL_AUTHORITY.equals(uri.getAuthority());
+ boolean result = uri != null && LAUNCH_CONTROL_AUTHORITY.equals(uri.getAuthority());
+ return result;
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
index ed7333d31149..14a33f9c1cc1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/ManageRootActivity.java
@@ -140,6 +140,7 @@ public class ManageRootActivity extends BaseActivity {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
+ Menus.disableHiddenItems(menu);
return true;
}
@@ -184,7 +185,7 @@ public class ManageRootActivity extends BaseActivity {
try {
startActivity(view);
} catch (ActivityNotFoundException ex2) {
- Shared.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT)
+ Snackbars.makeSnackbar(this, R.string.toast_no_application, Snackbar.LENGTH_SHORT)
.show();
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Menus.java b/packages/DocumentsUI/src/com/android/documentsui/Menus.java
new file mode 100644
index 000000000000..3f43a3d30c8d
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/Menus.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.documentsui;
+
+import android.view.Menu;
+import android.view.MenuItem;
+
+final class Menus {
+
+ private Menus() {}
+
+ /**
+ * Disables hidden menu items so that they are not invokable via command shortcuts
+ */
+ static void disableHiddenItems(Menu menu, MenuItem... exclusions) {
+ for (int i = 0; i < menu.size(); i++) {
+ MenuItem item = menu.getItem(i);
+ if (item.isVisible()) {
+ continue;
+ }
+ if (contains(exclusions, item)) {
+ continue;
+ }
+ item.setEnabled(false);
+ }
+ }
+
+ private static boolean contains(MenuItem[] exclusions, MenuItem item) {
+ for (int x = 0; x < exclusions.length; x++) {
+ if (exclusions[x] == item) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index 29bcd240a1b8..a4d6dc57cd34 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -16,19 +16,13 @@
package com.android.documentsui;
-import static com.android.internal.util.Preconditions.checkNotNull;
-
-import android.app.Activity;
import android.content.Context;
-import android.support.design.widget.Snackbar;
-import android.view.View;
-/**
- * @hide
- */
+/** @hide */
public final class Shared {
public static final boolean DEBUG = true;
public static final String TAG = "Documents";
+ public static final String EXTRA_STACK = "com.android.documentsui.STACK";
/**
* Generates a formatted quantity string.
@@ -36,14 +30,4 @@ public final class Shared {
public static final String getQuantityString(Context context, int resourceId, int quantity) {
return context.getResources().getQuantityString(resourceId, quantity, quantity);
}
-
- public static final Snackbar makeSnackbar(Activity activity, int messageId, int duration) {
- return makeSnackbar(activity, activity.getResources().getText(messageId), duration);
- }
-
- public static final Snackbar makeSnackbar(Activity activity, CharSequence message, int duration)
- {
- final View view = checkNotNull(activity.findViewById(R.id.coordinator_layout));
- return Snackbar.make(view, message, duration);
- }
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java b/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java
new file mode 100644
index 000000000000..f48b298a4f2f
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/Snackbars.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.documentsui;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.app.Activity;
+import android.support.design.widget.Snackbar;
+import android.view.View;
+
+final class Snackbars {
+ private Snackbars() {}
+
+ public static final Snackbar makeSnackbar(Activity activity, int messageId, int duration) {
+ return Snackbars.makeSnackbar(activity, activity.getResources().getText(messageId), duration);
+ }
+
+ public static final Snackbar makeSnackbar(Activity activity, CharSequence message, int duration)
+ {
+ final View view = checkNotNull(activity.findViewById(R.id.coordinator_layout));
+ return Snackbar.make(view, message, duration);
+ }
+}
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
index 568e9e403960..fc42c3b12545 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java
@@ -425,7 +425,7 @@ public class CopyTest extends ServiceTestCase<CopyService> {
stack.push(DocumentInfo.fromUri(mResolver, dst));
final Intent copyIntent = new Intent(mContext, CopyService.class);
copyIntent.putParcelableArrayListExtra(CopyService.EXTRA_SRC_LIST, srcDocs);
- copyIntent.putExtra(CopyService.EXTRA_STACK, (Parcelable) stack);
+ copyIntent.putExtra(Shared.EXTRA_STACK, (Parcelable) stack);
// startService(copyIntent);
return copyIntent;
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 47e24e8e82f3..368f9f79a20b 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -13,3 +13,11 @@
-keep class com.android.systemui.statusbar.phone.PhoneStatusBar
-keep class com.android.systemui.statusbar.tv.TvStatusBar
-keep class com.android.systemui.recents.*
+
+-keepclassmembers class ** {
+ public void onBusEvent(**);
+ public void onInterprocessBusEvent(**);
+}
+-keepclassmembers class ** extends **.EventBus$InterprocessEvent {
+ public <init>(android.os.Bundle);
+} \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background.xml b/packages/SystemUI/res/drawable/qs_customizer_background.xml
new file mode 100644
index 000000000000..6bb27cc2a517
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_customizer_background.xml
@@ -0,0 +1,19 @@
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@color/qs_detail_transition" />
+ <item android:drawable="?android:attr/windowBackground" />
+</transition>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index dc928c77baaa..59fed5ba7d2a 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -20,7 +20,8 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/navigation_bar_size"
- android:background="?android:attr/windowBackground">
+ android:background="@drawable/qs_customizer_background"
+ android:gravity="center_horizontal">
<FrameLayout
android:layout_width="match_parent"
@@ -76,10 +77,10 @@
</FrameLayout>
<com.android.systemui.tuner.AutoScrollView
- android:layout_width="match_parent"
+ android:layout_width="@dimen/notification_panel_width"
android:layout_height="0dp"
android:layout_weight="1"
- android:paddingTop="8dp"
+ android:paddingTop="12dp"
android:paddingBottom="8dp"
android:elevation="2dp">
@@ -87,7 +88,9 @@
android:id="@+id/quick_settings_panel"
android:background="#0000"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/notification_side_padding"
+ android:layout_marginRight="@dimen/notification_side_padding" />
</com.android.systemui.tuner.AutoScrollView>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index f8fc232106bb..b69fc0f4a8d8 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -325,8 +325,8 @@
<string name="interruption_level_priority_twoline" msgid="1564715335217164124">"Միայն\nկարևորները"</string>
<string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"Միայն\nզարթուցիչ"</string>
<string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> մինչև լրիվ լիցքավորումը)"</string>
- <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Արագ լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>՝ մինչև լցվելը)"</string>
- <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Դանդաղ լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>՝ մինչև լցվելը)"</string>
+ <string name="keyguard_indication_charging_time_fast" msgid="9018981952053914986">"Արագ լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>՝ մինչև ավարտ)"</string>
+ <string name="keyguard_indication_charging_time_slowly" msgid="955252797961724952">"Դանդաղ լիցքավորում (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>՝ մինչև ավարտ)"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Անջատել օգտվողին"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Փոխել օգտվողին. ներկայիս օգտվողն է՝ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_inactive" msgid="1424081831468083402">"Ընթացիկ օգտվողը՝ <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index d91335c28279..732312684fba 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -393,7 +393,7 @@
<string name="accessibility_volume_expand" msgid="5946812790999244205">"Utöka"</string>
<string name="accessibility_volume_collapse" msgid="3609549593031810875">"Komprimera"</string>
<string name="screen_pinning_title" msgid="3273740381976175811">"Skärmen har fästs"</string>
- <string name="screen_pinning_description" msgid="1346522416878235405">"Detta visar skärmen tills du lossar den. Tryck länge på bakåtknappen och Översikt samtidigt om du vill lossa skärmen."</string>
+ <string name="screen_pinning_description" msgid="1346522416878235405">"Med den här funktionen är skärmen synlig tills du lossar den. Tryck länge på Tillbaka och Översikt samtidigt om du vill lossa skärmen."</string>
<string name="screen_pinning_description_accessible" msgid="8518446209564202557">"Detta visar skärmen tills du lossar den. Tryck länge på Översikt om du vill lossa skärmen."</string>
<string name="screen_pinning_positive" msgid="3783985798366751226">"OK"</string>
<string name="screen_pinning_negative" msgid="3741602308343880268">"Nej tack"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 1d1958943e94..bae801716c19 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -222,7 +222,7 @@
<bool name="doze_pulse_on_notifications">true</bool>
<!-- Doze: when to pulse after a buzzworthy notification arrives -->
- <string name="doze_pulse_schedule" translatable="false">1s,10s,30s,60s</string>
+ <string name="doze_pulse_schedule" translatable="false">10s,30s,60s</string>
<!-- Doze: maximum number of times the notification pulse schedule can be reset -->
<integer name="doze_pulse_schedule_resets">2</integer>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 36efeaddd004..3370091f53c2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -88,6 +88,7 @@ public class DozeService extends DreamService {
private boolean mPowerSaveActive;
private boolean mCarMode;
private long mNotificationPulseTime;
+ private long mLastScheduleResetTime;
private long mEarliestPulseDueToLight;
private int mScheduleResetsRemaining;
@@ -356,13 +357,21 @@ public class DozeService extends DreamService {
return;
}
final long pulseDuration = mDozeParameters.getPulseDuration(false /*pickup*/);
- if ((notificationTimeMs - mNotificationPulseTime) < pulseDuration) {
+ boolean pulseImmediately = System.currentTimeMillis() >= notificationTimeMs;
+ if ((notificationTimeMs - mLastScheduleResetTime) >= pulseDuration) {
+ mScheduleResetsRemaining--;
+ mLastScheduleResetTime = notificationTimeMs;
+ } else if (!pulseImmediately){
if (DEBUG) Log.d(mTag, "Recently updated, not resetting schedule");
return;
}
- mScheduleResetsRemaining--;
if (DEBUG) Log.d(mTag, "mScheduleResetsRemaining = " + mScheduleResetsRemaining);
mNotificationPulseTime = notificationTimeMs;
+ if (pulseImmediately) {
+ DozeLog.traceNotificationPulse(0);
+ requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
+ }
+ // schedule the rest of the pulses
rescheduleNotificationPulse(true /*predicate*/);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index c612600ac82e..32c906e46afb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -181,6 +181,7 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout {
public TilePage(Context context, AttributeSet attrs) {
super(context, attrs);
mAllowDual = false;
+ updateResources();
}
public void setMaxRows(int maxRows) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index ca38528b05d0..47189b023132 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -140,13 +140,13 @@ public class QSFooter implements OnClickListener, DialogInterface.OnClickListene
mDialog.setTitle(getTitle(deviceOwner));
mDialog.setMessage(getMessage(deviceOwner, profileOwner, primaryVpn, profileVpn, managed));
mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
- if (mSecurityController.isVpnEnabled()) {
- mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getNegativeButton(), this);
+ if (mSecurityController.isVpnEnabled() && !mSecurityController.isVpnRestricted()) {
+ mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
}
mDialog.show();
}
- private String getNegativeButton() {
+ private String getSettingsButton() {
return mContext.getString(R.string.status_bar_settings_settings_button);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 880349e232fe..18af35ea61cb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -172,7 +172,8 @@ public class QSPanel extends FrameLayout implements Tunable {
mCustomizePanel.setHost(mHost);
} else {
if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
- mCustomizePanel.hide();
+ mCustomizePanel.hide(mCustomizePanel.getWidth() / 2,
+ mCustomizePanel.getHeight() / 2);
}
mCustomizePanel = null;
}
@@ -242,7 +243,7 @@ public class QSPanel extends FrameLayout implements Tunable {
public void onCollapse() {
if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
- mCustomizePanel.hide();
+ mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
}
}
@@ -382,7 +383,12 @@ public class QSPanel extends FrameLayout implements Tunable {
public boolean onLongClick(View v) {
if (mCustomizePanel != null) {
if (!mCustomizePanel.isCustomizing()) {
- mCustomizePanel.show();
+ int[] loc = new int[2];
+ getLocationInWindow(loc);
+ int x = r.tileView.getLeft() + r.tileView.getWidth() / 2 + loc[0];
+ int y = r.tileView.getTop() + mTileLayout.getOffsetTop(r)
+ + r.tileView.getHeight() / 2 + loc[1];
+ mCustomizePanel.show(x, y);
}
} else {
r.tile.longClick();
@@ -409,7 +415,7 @@ public class QSPanel extends FrameLayout implements Tunable {
public void closeDetail() {
if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
// Treat this as a detail panel for now, to make things easy.
- mCustomizePanel.hide();
+ mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
return;
}
showDetail(false, mDetailRecord);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 8bd05fad5699..b8342e2ada7c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -68,9 +68,10 @@ public class TileLayout extends ViewGroup implements QSTileLayout {
final Resources res = mContext.getResources();
final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
mCellHeight = res.getDimensionPixelSize(R.dimen.qs_tile_height);
- mCellWidth = (int)(mCellHeight * TILE_ASPECT);
- mLargeCellHeight = res.getDimensionPixelSize(R.dimen.qs_dual_tile_height);
- mLargeCellWidth = (int)(mLargeCellHeight * TILE_ASPECT);
+ mCellWidth = (int) (mCellHeight * TILE_ASPECT);
+ mLargeCellHeight = mAllowDual ? res.getDimensionPixelSize(R.dimen.qs_dual_tile_height)
+ : mCellHeight;
+ mLargeCellWidth = mAllowDual ? (int) (mLargeCellHeight * TILE_ASPECT) : mCellWidth;
mDualTileUnderlap = res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical);
if (mColumns != columns) {
mColumns = columns;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
index f676ea3499cb..3491cb6fdf8a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSTileHost.java
@@ -160,6 +160,11 @@ public class CustomQSTileHost extends QSTileHost {
}
@Override
+ public boolean isVpnRestricted() {
+ return false;
+ }
+
+ @Override
public String getPrimaryVpnName() {
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 7e7478545a07..601961b41a16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -15,23 +15,19 @@
*/
package com.android.systemui.qs.customize;
+import android.animation.Animator;
import android.content.ClipData;
import android.content.Context;
import android.util.AttributeSet;
import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.DragEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
+import android.view.*;
import android.view.View.OnClickListener;
-import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Toolbar;
import android.widget.Toolbar.OnMenuItemClickListener;
-
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
+import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.qs.QSTile.Host.Callback;
import com.android.systemui.qs.customize.DropButton.OnDropListener;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -48,10 +44,11 @@ import java.util.ArrayList;
* *someday* do fancy animations to get into/out of it.
*/
public class QSCustomizer extends LinearLayout implements OnMenuItemClickListener, Callback,
- OnDropListener, OnClickListener {
+ OnDropListener, OnClickListener, Animator.AnimatorListener {
private static final int MENU_SAVE = Menu.FIRST;
private static final int MENU_RESET = Menu.FIRST + 1;
+ private final QSDetailClipper mClipper;
private PhoneStatusBar mPhoneStatusBar;
@@ -69,6 +66,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
super(new ContextThemeWrapper(context, android.R.style.Theme_Material), attrs);
mPhoneStatusBar = ((SystemUIApplication) mContext.getApplicationContext())
.getComponent(PhoneStatusBar.class);
+ mClipper = new QSDetailClipper(this);
}
public void setHost(QSTileHost host) {
@@ -90,8 +88,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
mToolbar.setNavigationOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- // TODO: Is this all we want...?
- hide();
+ hide(0, 0);
}
});
mToolbar.setOnMenuItemClickListener(this);
@@ -115,17 +112,18 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
mFab.setOnClickListener(this);
}
- public void show() {
+ public void show(int x, int y) {
isShown = true;
mHost.setSavedTiles();
- // TODO: Fancy shmancy reveal.
mPhoneStatusBar.getStatusBarWindow().addView(this);
+ mQsPanel.setListening(true);
+ mClipper.animateCircularClip(x, y, true, this);
}
- public void hide() {
+ public void hide(int x, int y) {
isShown = false;
- // TODO: Similarly awesome or better hide.
- mPhoneStatusBar.getStatusBarWindow().removeView(this);
+ mQsPanel.setListening(false);
+ mClipper.animateCircularClip(x, y, false, this);
}
public boolean isCustomizing() {
@@ -146,7 +144,8 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
private void save() {
mHost.saveCurrentTiles();
- hide();
+ // TODO: At save button.
+ hide(0, 0);
}
@Override
@@ -197,4 +196,28 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene
// TODO: Show list of tiles.
}
}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!isShown) {
+ mPhoneStatusBar.getStatusBarWindow().removeView(this);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (!isShown) {
+ mPhoneStatusBar.getStatusBarWindow().removeView(this);
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Don't care.
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ // Don't care.
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 23cc8f067d83..b1cc27a3445a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -74,6 +74,8 @@ import java.util.ArrayList;
public class Recents extends SystemUI
implements ActivityOptions.OnAnimationStartedListener, RecentsComponent {
+ public final static int EVENT_BUS_PRIORITY = 1;
+
final public static String EXTRA_TRIGGERED_FROM_ALT_TAB = "triggeredFromAltTab";
final public static String EXTRA_TRIGGERED_FROM_HOME_KEY = "triggeredFromHomeKey";
final public static String EXTRA_RECENTS_VISIBILITY = "recentsVisibility";
@@ -114,9 +116,8 @@ public class Recents extends SystemUI
/** Preloads the next task */
public void run() {
- // Temporarily skip this if multi stack is enabled
- if (mConfig.multiWindowEnabled) return;
-
+ // TODO: Temporarily skip this if multi stack is enabled
+ /*
RecentsConfiguration config = RecentsConfiguration.getInstance();
if (config.svelteLevel == RecentsConfiguration.SVELTE_NONE) {
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
@@ -125,7 +126,7 @@ public class Recents extends SystemUI
// Load the next task only if we aren't svelte
RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
- loader.preloadTasks(plan, true /* isTopTaskHome */);
+ loader.preloadTasks(plan, true);
RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
// This callback is made when a new activity is launched and the old one is paused
// so ignore the current activity and try and preload the thumbnail for the
@@ -139,6 +140,7 @@ public class Recents extends SystemUI
launchOpts.onlyLoadPausedActivities = true;
loader.loadTasks(mContext, plan, launchOpts);
}
+ */
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 9ce6b2c764b4..e647c1f6dcfe 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -19,23 +19,35 @@ package com.android.systemui.recents;
import android.app.Activity;
import android.app.ActivityOptions;
import android.app.SearchManager;
+import android.app.TaskStackBuilder;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.net.Uri;
import android.os.Bundle;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.provider.Settings;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewStub;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
+import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.ResizeTaskEvent;
+import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.misc.Console;
import com.android.systemui.recents.misc.ReferenceCountedTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
@@ -44,16 +56,17 @@ import com.android.systemui.recents.views.RecentsView;
import com.android.systemui.recents.views.SystemBarScrimViews;
import com.android.systemui.recents.views.ViewAnimation;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
/**
* The main Recents activity that is started from AlternateRecentsComponent.
*/
-public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
- RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks {
+public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks {
+
+ public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
RecentsConfiguration mConfig;
+ RecentsPackageMonitor mPackageMonitor;
long mLastTabKeyEventTime;
// Top level views
@@ -98,22 +111,11 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
@Override
public void run() {
- // Finish Recents
- if (mLaunchIntent != null) {
- try {
- if (mLaunchOpts != null) {
- startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT);
- } else {
- startActivityAsUser(mLaunchIntent, UserHandle.CURRENT);
- }
- } catch (Exception e) {
- Console.logError(RecentsActivity.this,
- getString(R.string.recents_launch_error_message, "Home"));
- }
- } else {
- finish();
- overridePendingTransition(R.anim.recents_to_launcher_enter,
- R.anim.recents_to_launcher_exit);
+ try {
+ startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT);
+ } catch (Exception e) {
+ Console.logError(RecentsActivity.this,
+ getString(R.string.recents_launch_error_message, "Home"));
}
}
}
@@ -131,7 +133,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
dismissRecentsToFocusedTaskOrHome(false);
} else if (intent.getBooleanExtra(Recents.EXTRA_TRIGGERED_FROM_HOME_KEY, false)) {
// Otherwise, dismiss Recents to Home
- dismissRecentsToHomeRaw(true);
+ dismissRecentsToHome(true);
} else {
// Do nothing
}
@@ -157,7 +159,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
String action = intent.getAction();
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
// When the screen turns off, dismiss Recents to Home
- dismissRecentsToHome(false);
+ dismissRecentsToHomeIfVisible(false);
} else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
// When the search activity changes, update the search widget view
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
@@ -273,25 +275,28 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
if (mRecentsView.launchFocusedTask()) return true;
// If we launched from Home, then return to Home
if (launchState.launchedFromHome) {
- dismissRecentsToHomeRaw(true);
+ dismissRecentsToHome(true);
return true;
}
// Otherwise, try and return to the Task that Recents was launched from
if (mRecentsView.launchPreviousTask()) return true;
// If none of the other cases apply, then just go Home
- dismissRecentsToHomeRaw(true);
+ dismissRecentsToHome(true);
return true;
}
return false;
}
- /** Dismisses Recents directly to Home. */
- void dismissRecentsToHomeRaw(boolean animated) {
+ /**
+ * Dismisses Recents directly to Home without checking whether it is currently visible.
+ */
+ void dismissRecentsToHome(boolean animated) {
if (animated) {
ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
null, mFinishLaunchHomeRunnable, null);
mRecentsView.startExitToHomeAnimation(
new ViewAnimation.TaskViewExitContext(exitTrigger));
+ mScrimViews.startExitRecentsAnimation();
} else {
mFinishLaunchHomeRunnable.run();
}
@@ -304,11 +309,11 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
}
/** Dismisses Recents directly to Home if we currently aren't transitioning. */
- boolean dismissRecentsToHome(boolean animated) {
+ boolean dismissRecentsToHomeIfVisible(boolean animated) {
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
// Return to Home
- dismissRecentsToHomeRaw(animated);
+ dismissRecentsToHome(animated);
return true;
}
return false;
@@ -318,12 +323,17 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ // Register this activity with the event bus
+ EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
+
// For the non-primary user, ensure that the SystemServicesProxy and configuration is
// initialized
RecentsTaskLoader.initialize(this);
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
mConfig = RecentsConfiguration.initialize(this, ssp);
mConfig.update(this, ssp, ssp.getWindowRect());
+ mPackageMonitor = new RecentsPackageMonitor();
// Initialize the widget host (the host id is static and does not change)
mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
@@ -371,7 +381,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
registerReceiver(mServiceBroadcastReceiver, filter);
// Register any broadcast receivers for the task loader
- loader.registerReceivers(this, mRecentsView);
+ mPackageMonitor.register(this);
// Update the recent tasks
updateRecentsTasks();
@@ -403,7 +413,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
protected void onStop() {
super.onStop();
MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
SystemServicesProxy ssp = loader.getSystemServicesProxy();
Recents.notifyVisibilityChanged(this, ssp, false);
@@ -415,11 +424,12 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
unregisterReceiver(mServiceBroadcastReceiver);
// Unregister any broadcast receivers for the task loader
- loader.unregisterReceivers();
+ mPackageMonitor.unregister();
// Workaround for b/22542869, if the RecentsActivity is started again, but without going
// through SystemUI, we need to reset the config launch flags to ensure that we do not
// wait on the system to send a signal that was never queued.
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
launchState.launchedFromHome = false;
launchState.launchedFromSearchHome = false;
launchState.launchedFromAppWithThumbnail = false;
@@ -437,6 +447,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
// Stop listening for widget package changes if there was one bound
mAppWidgetHost.stopListening();
+ EventBus.getDefault().unregister(this);
}
public void onEnterAnimationTriggered() {
@@ -446,16 +457,12 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
mRecentsView.startEnterRecentsAnimation(ctx);
if (mSearchWidgetInfo != null) {
- final WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks> cbRef =
- new WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks>(
- RecentsActivity.this);
ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
@Override
public void run() {
// Start listening for widget package changes if there is one bound
- RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks cb = cbRef.get();
- if (cb != null) {
- mAppWidgetHost.startListening(cb);
+ if (mAppWidgetHost != null) {
+ mAppWidgetHost.startListening();
}
}
});
@@ -530,11 +537,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
return mResizeTaskDebugDialog;
}
- @Override
- public void onTaskResize(Task t) {
- getResizeTaskDebugDialog().showResizeTaskDialog(t, mRecentsView);
- }
-
/**** RecentsView.RecentsViewCallbacks Implementation ****/
@Override
@@ -550,7 +552,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
@Override
public void onTaskLaunchFailed() {
// Return to Home
- dismissRecentsToHomeRaw(true);
+ dismissRecentsToHome(true);
}
@Override
@@ -572,10 +574,52 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView
mAfterPauseRunnable = r;
}
- /**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/
+ /**** EventBus events ****/
- @Override
- public void refreshSearchWidgetView() {
+ public final void onBusEvent(AppWidgetProviderChangedEvent event) {
+ refreshSearchWidgetView();
+ }
+
+ public final void onBusEvent(ShowApplicationInfoEvent event) {
+ // Create a new task stack with the application info details activity
+ Intent baseIntent = event.task.key.baseIntent;
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+ Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
+ intent.setComponent(intent.resolveActivity(getPackageManager()));
+ TaskStackBuilder.create(this)
+ .addNextIntentWithParentStack(intent).startActivities(null,
+ new UserHandle(event.task.key.userId));
+
+ // Keep track of app-info invocations
+ MetricsLogger.count(this, "overview_app_info", 1);
+ }
+
+ public final void onBusEvent(DismissTaskEvent event) {
+ // Remove any stored data from the loader
+ RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
+ loader.deleteTaskData(event.task, false);
+
+ // Remove the task from activity manager
+ loader.getSystemServicesProxy().removeTask(event.task.key.id);
+ }
+
+ public final void onBusEvent(ResizeTaskEvent event) {
+ getResizeTaskDebugDialog().showResizeTaskDialog(event.task, mRecentsView);
+ }
+
+ public final void onBusEvent(DragStartEvent event) {
+ // Lock the orientation while dragging
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+
+ // TODO: docking requires custom accessibility actions
+ }
+
+ public final void onBusEvent(DragEndEvent event) {
+ // Unlock the orientation when dragging completes
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_BEHIND);
+ }
+
+ private void refreshSearchWidgetView() {
if (mSearchWidgetInfo != null) {
SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
int searchWidgetId = ssp.getSearchAppWidgetId(this);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
index 0102332b17cb..fc96c11e4124 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
@@ -20,24 +20,19 @@ import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
/** Our special app widget host for the Search widget */
public class RecentsAppWidgetHost extends AppWidgetHost {
- /* Callbacks to notify when an app package changes */
- interface RecentsAppWidgetHostCallbacks {
- void refreshSearchWidgetView();
- }
-
- RecentsAppWidgetHostCallbacks mCb;
boolean mIsListening;
public RecentsAppWidgetHost(Context context, int hostId) {
super(context, hostId);
}
- public void startListening(RecentsAppWidgetHostCallbacks cb) {
- mCb = cb;
+ public void startListening() {
if (!mIsListening) {
mIsListening = true;
super.startListening();
@@ -47,11 +42,9 @@ public class RecentsAppWidgetHost extends AppWidgetHost {
@Override
public void stopListening() {
if (mIsListening) {
+ mIsListening = false;
super.stopListening();
}
- // Ensure that we release any references to the callbacks
- mCb = null;
- mIsListening = false;
}
@Override
@@ -66,8 +59,8 @@ public class RecentsAppWidgetHost extends AppWidgetHost {
@Override
protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
super.onProviderChanged(appWidgetId, appWidgetInfo);
- if (mIsListening && mCb != null) {
- mCb.refreshSearchWidgetView();
+ if (mIsListening) {
+ EventBus.getDefault().send(new AppWidgetProviderChangedEvent());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 52b9521b9952..b6f4a3c61383 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -22,6 +22,7 @@ import android.graphics.Rect;
import android.provider.Settings;
import com.android.systemui.R;
import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.model.RecentsTaskLoader;
/**
* Application resources that can be retrieved from the application context and are not specifically
@@ -70,13 +71,13 @@ public class RecentsConfiguration {
public final int smallestWidth;
/** Misc **/
+ public boolean hasDockedTasks;
public boolean useHardwareLayers;
public boolean fakeShadows;
public int svelteLevel;
public int searchBarSpaceHeightPx;
/** Dev options and global settings */
- public boolean multiWindowEnabled;
public boolean lockToAppEnabled;
/** Private constructor */
@@ -113,7 +114,7 @@ public class RecentsConfiguration {
// settings or via multi window
lockToAppEnabled = ssp.getSystemSetting(context,
Settings.System.LOCK_TO_APP_ENABLED) != 0;
- multiWindowEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
+ hasDockedTasks = ssp.hasDockedTask();
// Recompute some values based on the given state, since we can not rely on the resource
// system to get certain values.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index 59df293f71d7..88270650ca1c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -235,11 +235,9 @@ public class RecentsResizeTaskDialog extends DialogFragment {
// In debug mode, we force all task to be resizeable regardless of the
// current app configuration.
- if (RecentsConfiguration.getInstance().multiWindowEnabled) {
- for (int i = additionalTasks; i >= 0; --i) {
- if (mTasks[i] != null) {
- mSsp.setTaskResizeable(mTasks[i].key.id);
- }
+ for (int i = additionalTasks; i >= 0; --i) {
+ if (mTasks[i] != null) {
+ mSsp.setTaskResizeable(mTasks[i].key.id);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
new file mode 100644
index 000000000000..ef543d0715f7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -0,0 +1,844 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.MutableBoolean;
+
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Represents a subscriber, which implements various event bus handler methods.
+ */
+class Subscriber {
+ private WeakReference<Object> mSubscriber;
+
+ long registrationTime;
+
+ Subscriber(Object subscriber, long registrationTime) {
+ mSubscriber = new WeakReference<>(subscriber);
+ this.registrationTime = registrationTime;
+ }
+
+ public String toString(int priority) {
+ Object sub = mSubscriber.get();
+ String id = Integer.toHexString(System.identityHashCode(sub));
+ return sub.getClass().getSimpleName() + " [0x" + id + ", P" + priority + "]";
+ }
+
+ public Object getReference() {
+ return mSubscriber.get();
+ }
+}
+
+/**
+ * Represents an event handler with a priority.
+ */
+class EventHandler {
+ int priority;
+ Subscriber subscriber;
+ EventHandlerMethod method;
+
+ EventHandler(Subscriber subscriber, EventHandlerMethod method, int priority) {
+ this.subscriber = subscriber;
+ this.method = method;
+ this.priority = priority;
+ }
+
+ @Override
+ public String toString() {
+ return subscriber.toString(priority) + " " + method.toString();
+ }
+}
+
+/**
+ * Represents the low level method handling a particular event.
+ */
+class EventHandlerMethod {
+ private Method mMethod;
+ Class<? extends EventBus.Event> eventType;
+
+ EventHandlerMethod(Method method, Class<? extends EventBus.Event> eventType) {
+ mMethod = method;
+ mMethod.setAccessible(true);
+ this.eventType = eventType;
+ }
+
+ public void invoke(Object target, EventBus.Event event)
+ throws InvocationTargetException, IllegalAccessException {
+ mMethod.invoke(target, event);
+ }
+
+ @Override
+ public String toString() {
+ return mMethod.getName() + "(" + eventType.getSimpleName() + ")";
+ }
+}
+
+/**
+ * A simple in-process event bus. It is simple because we can make assumptions about the state of
+ * SystemUI and Recent's lifecycle.
+ *
+ * <p>
+ * Currently, there is a single EventBus that handles {@link EventBus.Event}s for each subscriber
+ * on the main application thread. Publishers can send() events to synchronously call subscribers
+ * of that event, or post() events to be processed in the next run of the {@link Looper}. In
+ * addition, the EventBus supports sending and handling {@link EventBus.InterprocessEvent}s
+ * (within the same package) implemented using standard {@link BroadcastReceiver} mechanism.
+ * Interprocess events must be posted using postInterprocess() to ensure that it is dispatched
+ * correctly across processes.
+ *
+ * <p>
+ * Subscribers must be registered with a particular EventBus before they will receive events, and
+ * handler methods must match a specific signature.
+ *
+ * <p>
+ * Event method signature:<ul>
+ * <li>Methods must be public final
+ * <li>Methods must return void
+ * <li>Methods must be called "onBusEvent"
+ * <li>Methods must take one parameter, of class type deriving from {@link EventBus.Event}
+ * </ul>
+ *
+ * <p>
+ * Interprocess-Event method signature:<ul>
+ * <li>Methods must be public final
+ * <li>Methods must return void
+ * <li>Methods must be called "onInterprocessBusEvent"
+ * <li>Methods must take one parameter, of class type deriving from {@link EventBus.InterprocessEvent}
+ * </ul>
+ * </p>
+ *
+ * </p>
+ * Each subscriber can be registered with a given priority (default 1), and events will be dispatch
+ * in decreasing order of priority. For subscribers with the same priority, events will be
+ * dispatched by latest registration time to earliest.
+ *
+ * <p>
+ * Interprocess events must extend {@link EventBus.InterprocessEvent}, have a constructor which
+ * takes a {@link Bundle} and implement toBundle(). This allows us to serialize events to be sent
+ * across processes.
+ *
+ * <p>
+ * Caveats:<ul>
+ * <li>The EventBus keeps a {@link WeakReference} to the publisher to prevent memory leaks, so
+ * there must be another strong reference to the publisher for it to not get garbage-collected and
+ * continue receiving events.
+ * <li>Because the event handlers are called back using reflection, the EventBus is not intended
+ * for use in tight, performance criticial loops. For most user input/system callback events, this
+ * is generally of low enough frequency to use the EventBus.
+ * <li>Because the event handlers are called back using reflection, there will often be no
+ * references to them from actual code. The proguard configuration will be need to be updated to
+ * keep these extra methods:
+ *
+ * -keepclassmembers class ** {
+ * public void onBusEvent(**);
+ * public void onInterprocessBusEvent(**);
+ * }
+ * -keepclassmembers class ** extends **.EventBus$InterprocessEvent {
+ * public <init>(android.os.Bundle);
+ * }
+ *
+ * <li>Subscriber registration can be expensive depending on the subscriber's {@link Class}. This
+ * is only done once per class type, but if possible, it is best to pre-register an instance of
+ * that class beforehand or when idle.
+ * <li>Each event should be sent once. Events may hold internal information about the current
+ * dispatch, or may be queued to be dispatched on another thread (if posted from a non-main thread),
+ * so it may be unsafe to edit, change, or re-send the event again.
+ * <li>Events should follow a pattern of public-final POD (plain old data) objects, where they are
+ * initialized by the constructor and read by each subscriber of that event. Subscribers should
+ * never alter events as they are processed, and this enforces that pattern.
+ * </ul>
+ *
+ * <p>
+ * Future optimizations:
+ * <li>throw exception/log when a subscriber loses the reference
+ * <li>trace cost per registration & invocation
+ * <li>trace cross-process invocation
+ * <li>register(subscriber, Class&lt;?&gt;...) -- pass in exact class types you want registered
+ * <li>setSubscriberEventHandlerPriority(subscriber, Class<Event>, priority)
+ * <li>allow subscribers to implement interface, ie. EventBus.Subscriber, which lets then test a
+ * message before invocation (ie. check if task id == this task id)
+ * <li>add postOnce() which automatically debounces
+ * <li>add postDelayed() which delays / postDelayedOnce() which delays and bounces
+ * <li>consolidate register() and registerInterprocess()
+ * <li>sendForResult&lt;ReturnType&gt;(Event) to send and get a result, but who will send the
+ * result?
+ * </p>
+ */
+public class EventBus extends BroadcastReceiver {
+
+ public static final String TAG = "EventBus";
+
+ /**
+ * An event super class that allows us to track internal event state across subscriber
+ * invocations.
+ *
+ * Events should not be edited by subscribers.
+ */
+ public static class Event {
+ // Indicates that this event's dispatch should be traced and logged to logcat
+ boolean trace;
+ // Indicates that this event must be posted on the EventBus's looper thread before invocation
+ boolean requiresPost;
+ // Not currently exposed, allows a subscriber to cancel further dispatch of this event
+ boolean cancelled;
+
+ // Only accessible from derived events
+ protected Event() {}
+ }
+
+ /**
+ * An inter-process event super class that allows us to track user state across subscriber
+ * invocations.
+ */
+ public static class InterprocessEvent extends Event {
+ private static final String EXTRA_USER = "_user";
+
+ // The user which this event originated from
+ public final int user;
+
+ // Only accessible from derived events
+ protected InterprocessEvent(int user) {
+ this.user = user;
+ }
+
+ /**
+ * Called from the event bus
+ */
+ protected InterprocessEvent(Bundle b) {
+ user = b.getInt(EXTRA_USER);
+ }
+
+ protected Bundle toBundle() {
+ Bundle b = new Bundle();
+ b.putInt(EXTRA_USER, user);
+ return b;
+ }
+ }
+
+ /**
+ * Proguard must also know, and keep, all methods matching this signature.
+ *
+ * -keepclassmembers class ** {
+ * public void onBusEvent(**);
+ * public void onInterprocessBusEvent(**);
+ * }
+ */
+ private static final String METHOD_PREFIX = "onBusEvent";
+ private static final String INTERPROCESS_METHOD_PREFIX = "onInterprocessBusEvent";
+
+ // Ensures that interprocess events can only be sent from a process holding this permission. */
+ private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+
+ // Used for passing event data across process boundaries
+ private static final String EXTRA_INTERPROCESS_EVENT_BUNDLE = "interprocess_event_bundle";
+
+ // The default priority of all subscribers
+ private static final int DEFAULT_SUBSCRIBER_PRIORITY = 1;
+
+ // Used for debugging everything
+ private static final boolean DEBUG_TRACE_ALL = false;
+
+ // Orders the handlers by priority and registration time
+ private static final Comparator<EventHandler> EVENT_HANDLER_COMPARATOR = new Comparator<EventHandler>() {
+ @Override
+ public int compare(EventHandler h1, EventHandler h2) {
+ // Rank the handlers by priority descending, followed by registration time descending.
+ // aka. the later registered
+ if (h1.priority != h2.priority) {
+ return h2.priority - h1.priority;
+ } else {
+ return Long.compare(h2.subscriber.registrationTime, h1.subscriber.registrationTime);
+ }
+ }
+ };
+
+ // Used for initializing the default bus
+ private static final Object sLock = new Object();
+ private static EventBus sDefaultBus;
+
+ // The handler to post all events
+ private Handler mHandler;
+
+ // Keep track of whether we have registered a broadcast receiver already, so that we can
+ // unregister ourselves before re-registering again with a new IntentFilter.
+ private boolean mHasRegisteredReceiver;
+
+ /**
+ * Map from event class -> event handler list. Keeps track of the actual mapping from event
+ * to subscriber method.
+ */
+ private HashMap<Class<? extends Event>, ArrayList<EventHandler>> mEventTypeMap = new HashMap<>();
+
+ /**
+ * Map from subscriber class -> event handler method lists. Used to determine upon registration
+ * of a new subscriber whether we need to read all the subscriber's methods again using
+ * reflection or whether we can just add the subscriber to the event type map.
+ */
+ private HashMap<Class<? extends Object>, ArrayList<EventHandlerMethod>> mSubscriberTypeMap = new HashMap<>();
+
+ /**
+ * Map from interprocess event name -> interprocess event class. Used for mapping the event
+ * name after receiving the broadcast, to the event type. After which a new instance is created
+ * and posted in the local process.
+ */
+ private HashMap<String, Class<? extends InterprocessEvent>> mInterprocessEventNameMap = new HashMap<>();
+
+ /**
+ * Set of all currently registered subscribers
+ */
+ private ArrayList<Subscriber> mSubscribers = new ArrayList<>();
+
+ // For tracing
+ private int mCallCount;
+ private long mCallDurationMicros;
+
+ /**
+ * Private constructor to create an event bus for a given looper.
+ */
+ private EventBus(Looper looper) {
+ mHandler = new Handler(looper);
+ }
+
+ /**
+ * @return the default event bus for the application's main thread.
+ */
+ public static EventBus getDefault() {
+ if (sDefaultBus == null)
+ synchronized (sLock) {
+ if (sDefaultBus == null) {
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("New EventBus");
+ }
+ sDefaultBus = new EventBus(Looper.getMainLooper());
+ }
+ }
+ return sDefaultBus;
+ }
+
+ /**
+ * Registers a subscriber to receive events with the default priority.
+ *
+ * @param subscriber the subscriber to handle events. If this is the first instance of the
+ * subscriber's class type that has been registered, the class's methods will
+ * be scanned for appropriate event handler methods.
+ */
+ public void register(Object subscriber) {
+ registerSubscriber(subscriber, DEFAULT_SUBSCRIBER_PRIORITY, null);
+ }
+
+ /**
+ * Registers a subscriber to receive events with the given priority.
+ *
+ * @param subscriber the subscriber to handle events. If this is the first instance of the
+ * subscriber's class type that has been registered, the class's methods will
+ * be scanned for appropriate event handler methods.
+ * @param priority the priority that this subscriber will receive events relative to other
+ * subscribers
+ */
+ public void register(Object subscriber, int priority) {
+ registerSubscriber(subscriber, priority, null);
+ }
+
+ /**
+ * Explicitly registers a subscriber to receive interprocess events with the default priority.
+ *
+ * @param subscriber the subscriber to handle events. If this is the first instance of the
+ * subscriber's class type that has been registered, the class's methods will
+ * be scanned for appropriate event handler methods.
+ */
+ public void registerInterprocessAsCurrentUser(Context context, Object subscriber) {
+ registerInterprocessAsCurrentUser(context, subscriber, DEFAULT_SUBSCRIBER_PRIORITY);
+ }
+
+ /**
+ * Registers a subscriber to receive interprocess events with the given priority.
+ *
+ * @param subscriber the subscriber to handle events. If this is the first instance of the
+ * subscriber's class type that has been registered, the class's methods will
+ * be scanned for appropriate event handler methods.
+ * @param priority the priority that this subscriber will receive events relative to other
+ * subscribers
+ */
+ public void registerInterprocessAsCurrentUser(Context context, Object subscriber, int priority) {
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("registerInterprocessAsCurrentUser(" + subscriber.getClass().getSimpleName() + ")");
+ }
+
+ // Register the subscriber normally, and update the broadcast receiver filter if this is
+ // a new subscriber type with interprocess events
+ MutableBoolean hasInterprocessEventsChanged = new MutableBoolean(false);
+ registerSubscriber(subscriber, priority, hasInterprocessEventsChanged);
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("hasInterprocessEventsChanged: " + hasInterprocessEventsChanged.value);
+ }
+ if (hasInterprocessEventsChanged.value) {
+ registerReceiverForInterprocessEvents(context);
+ }
+ }
+
+ /**
+ * Remove all EventHandlers pointing to the specified subscriber. This does not remove the
+ * mapping of subscriber type to event handler method, in case new instances of this subscriber
+ * are registered.
+ */
+ public void unregister(Object subscriber) {
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("unregister()");
+ }
+
+ // Fail immediately if we are being called from the non-main thread
+ long callingThreadId = Thread.currentThread().getId();
+ if (callingThreadId != mHandler.getLooper().getThread().getId()) {
+ throw new RuntimeException("Can not unregister() a subscriber from a non-main thread.");
+ }
+
+ // Return early if this is not a registered subscriber
+ if (!findRegisteredSubscriber(subscriber, true /* removeFoundSubscriber */)) {
+ return;
+ }
+
+ Class<?> subscriberType = subscriber.getClass();
+ ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType);
+ if (subscriberMethods != null) {
+ // For each of the event handlers the subscriber handles, remove all references of that
+ // handler
+ for (EventHandlerMethod method : subscriberMethods) {
+ ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(method.eventType);
+ for (int i = eventHandlers.size() - 1; i >= 0; i--) {
+ if (eventHandlers.get(i).subscriber.getReference() == subscriber) {
+ eventHandlers.remove(i);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Explicit unregistration for interprocess event subscribers. This actually behaves exactly
+ * the same as unregister() since we also do not want to stop listening for specific
+ * inter-process messages in case new instances of that subscriber is registered.
+ */
+ public void unregisterInterprocess(Context context, Object subscriber) {
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("unregisterInterprocess()");
+ }
+ unregister(subscriber);
+ }
+
+ /**
+ * Sends an event to the subscribers of the given event type immediately. This can only be
+ * called from the same thread as the EventBus's looper thread (for the default EventBus, this
+ * is the main application thread).
+ */
+ public void send(Event event) {
+ // Fail immediately if we are being called from the non-main thread
+ long callingThreadId = Thread.currentThread().getId();
+ if (callingThreadId != mHandler.getLooper().getThread().getId()) {
+ throw new RuntimeException("Can not send() a message from a non-main thread.");
+ }
+
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("send(" + event.getClass().getSimpleName() + ")");
+ }
+
+ // Reset the event's cancelled state
+ event.requiresPost = false;
+ event.cancelled = false;
+ queueEvent(event);
+ }
+
+ /**
+ * Post a message to the subscribers of the given event type. The messages will be posted on
+ * the EventBus's looper thread (for the default EventBus, this is the main application thread).
+ */
+ public void post(Event event) {
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("post(" + event.getClass().getSimpleName() + ")");
+ }
+
+ // Reset the event's cancelled state
+ event.requiresPost = true;
+ event.cancelled = false;
+ queueEvent(event);
+ }
+
+ /** Prevent post()ing an InterprocessEvent */
+ @Deprecated
+ public void post(InterprocessEvent event) {
+ throw new RuntimeException("Not supported, use postInterprocess");
+ }
+
+ /** Prevent send()ing an InterprocessEvent */
+ @Deprecated
+ public void send(InterprocessEvent event) {
+ throw new RuntimeException("Not supported, use postInterprocess");
+ }
+
+ /**
+ * Posts an interprocess event.
+ */
+ public void postInterprocess(Context context, final InterprocessEvent event) {
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("postInterprocess(" + event.getClass().getSimpleName() + ")");
+ }
+ String eventType = event.getClass().getName();
+ Bundle eventBundle = event.toBundle();
+ Intent intent = new Intent(eventType);
+ intent.setPackage(context.getPackageName());
+ intent.putExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE, eventBundle);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT |
+ Intent.FLAG_RECEIVER_FOREGROUND);
+ context.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ /**
+ * Receiver for interprocess events.
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("onReceive(" + intent.getAction() + ", user " + UserHandle.myUserId() + ")");
+ }
+
+ Bundle eventBundle = intent.getBundleExtra(EXTRA_INTERPROCESS_EVENT_BUNDLE);
+ Class<? extends InterprocessEvent> eventType = mInterprocessEventNameMap.get(intent.getAction());
+ try {
+ Constructor<? extends InterprocessEvent> ctor = eventType.getConstructor(Bundle.class);
+ send((Event) ctor.newInstance(eventBundle));
+ } catch (NoSuchMethodException|
+ InvocationTargetException|
+ InstantiationException|
+ IllegalAccessException e) {
+ Log.e(TAG, "Failed to create InterprocessEvent", e);
+ }
+ }
+
+ /**
+ * @return a dump of the current state of the EventBus
+ */
+ public String dump() {
+ StringBuilder output = new StringBuilder();
+ output.append("Registered class types:");
+ output.append("\n");
+ for (Class<?> clz : mSubscriberTypeMap.keySet()) {
+ output.append("\t");
+ output.append(clz.getSimpleName());
+ output.append("\n");
+ }
+ output.append("Event map:");
+ output.append("\n");
+ for (Class<?> clz : mEventTypeMap.keySet()) {
+ output.append("\t");
+ output.append(clz.getSimpleName());
+ output.append(" -> ");
+ output.append("\n");
+ ArrayList<EventHandler> handlers = mEventTypeMap.get(clz);
+ for (EventHandler handler : handlers) {
+ Object subscriber = handler.subscriber.getReference();
+ if (subscriber != null) {
+ String id = Integer.toHexString(System.identityHashCode(subscriber));
+ output.append("\t\t");
+ output.append(subscriber.getClass().getSimpleName());
+ output.append(" [0x" + id + ", #" + handler.priority + "]");
+ output.append("\n");
+ }
+ }
+ }
+ return output.toString();
+ }
+
+ /**
+ * Registers a new subscriber.
+ *
+ * @return return whether or not this
+ */
+ private void registerSubscriber(Object subscriber, int priority,
+ MutableBoolean hasInterprocessEventsChangedOut) {
+ // Fail immediately if we are being called from the non-main thread
+ long callingThreadId = Thread.currentThread().getId();
+ if (callingThreadId != mHandler.getLooper().getThread().getId()) {
+ throw new RuntimeException("Can not register() a subscriber from a non-main thread.");
+ }
+
+ // Return immediately if this exact subscriber is already registered
+ if (findRegisteredSubscriber(subscriber, false /* removeFoundSubscriber */)) {
+ return;
+ }
+
+ long t1 = 0;
+ if (DEBUG_TRACE_ALL) {
+ t1 = SystemClock.currentTimeMicro();
+ logWithPid("registerSubscriber(" + subscriber.getClass().getSimpleName() + ")");
+ }
+ Subscriber sub = new Subscriber(subscriber, SystemClock.uptimeMillis());
+ Class<?> subscriberType = subscriber.getClass();
+ ArrayList<EventHandlerMethod> subscriberMethods = mSubscriberTypeMap.get(subscriberType);
+ if (subscriberMethods != null) {
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("Subscriber class type already registered");
+ }
+
+ // If we've parsed this subscriber type before, just add to the set for all the known
+ // events
+ for (EventHandlerMethod method : subscriberMethods) {
+ ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(method.eventType);
+ eventTypeHandlers.add(new EventHandler(sub, method, priority));
+ sortEventHandlersByPriority(eventTypeHandlers);
+ }
+ mSubscribers.add(sub);
+ return;
+ } else {
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("Subscriber class type requires registration");
+ }
+
+ // If we are parsing this type from scratch, ensure we add it to the subscriber type
+ // map, and pull out he handler methods below
+ subscriberMethods = new ArrayList<>();
+ mSubscriberTypeMap.put(subscriberType, subscriberMethods);
+ mSubscribers.add(sub);
+ }
+
+ // Find all the valid event bus handler methods of the subscriber
+ MutableBoolean isInterprocessEvent = new MutableBoolean(false);
+ Method[] methods = subscriberType.getDeclaredMethods();
+ for (Method m : methods) {
+ Class<?>[] parameterTypes = m.getParameterTypes();
+ isInterprocessEvent.value = false;
+ if (isValidEventBusHandlerMethod(m, parameterTypes, isInterprocessEvent)) {
+ Class<? extends Event> eventType = (Class<? extends Event>) parameterTypes[0];
+ ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(eventType);
+ if (eventTypeHandlers == null) {
+ eventTypeHandlers = new ArrayList<>();
+ mEventTypeMap.put(eventType, eventTypeHandlers);
+ }
+ if (isInterprocessEvent.value) {
+ try {
+ // Enforce that the event must have a Bundle constructor
+ eventType.getConstructor(Bundle.class);
+
+ mInterprocessEventNameMap.put(eventType.getName(),
+ (Class<? extends InterprocessEvent>) eventType);
+ if (hasInterprocessEventsChangedOut != null) {
+ hasInterprocessEventsChangedOut.value = true;
+ }
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("Expected InterprocessEvent to have a Bundle constructor");
+ }
+ }
+ EventHandlerMethod method = new EventHandlerMethod(m, eventType);
+ EventHandler handler = new EventHandler(sub, method, priority);
+ eventTypeHandlers.add(handler);
+ subscriberMethods.add(method);
+ sortEventHandlersByPriority(eventTypeHandlers);
+
+ if (DEBUG_TRACE_ALL) {
+ logWithPid(" * Method: " + m.getName() +
+ " event: " + parameterTypes[0].getSimpleName() +
+ " interprocess? " + isInterprocessEvent.value);
+ }
+ }
+ }
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("Registered " + subscriber.getClass().getSimpleName() + " in " +
+ (SystemClock.currentTimeMicro() - t1) + " microseconds");
+ }
+ }
+
+ /**
+ * Adds a new message.
+ */
+ private void queueEvent(final Event event) {
+ ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(event.getClass());
+ if (eventHandlers == null) {
+ return;
+ }
+ // We need to clone the list in case a subscriber unregisters itself during traversal
+ eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone();
+ for (final EventHandler eventHandler : eventHandlers) {
+ if (eventHandler.subscriber.getReference() != null) {
+ if (event.requiresPost) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ processEvent(eventHandler, event);
+ }
+ });
+ } else {
+ processEvent(eventHandler, event);
+ }
+ }
+ }
+ }
+
+ /**
+ * Processes and dispatches the given event to the given event handler, on the thread of whoever
+ * calls this method.
+ */
+ private void processEvent(final EventHandler eventHandler, final Event event) {
+ // Skip if the event was already cancelled
+ if (event.cancelled) {
+ if (event.trace || DEBUG_TRACE_ALL) {
+ logWithPid("Event dispatch cancelled");
+ }
+ return;
+ }
+
+ try {
+ if (event.trace || DEBUG_TRACE_ALL) {
+ logWithPid(" -> " + eventHandler.toString());
+ }
+ Object sub = eventHandler.subscriber.getReference();
+ if (sub != null) {
+ long t1 = 0;
+ if (DEBUG_TRACE_ALL) {
+ t1 = SystemClock.currentTimeMicro();
+ }
+ eventHandler.method.invoke(sub, event);
+ if (DEBUG_TRACE_ALL) {
+ long duration = (SystemClock.currentTimeMicro() - t1);
+ mCallDurationMicros += duration;
+ mCallCount++;
+ logWithPid(eventHandler.method.toString() + " duration: " + duration +
+ " microseconds, avg: " + (mCallDurationMicros / mCallCount));
+ }
+ } else {
+ Log.e(TAG, "Failed to deliver event to null subscriber");
+ }
+ } catch (IllegalAccessException e) {
+ Log.e(TAG, "Failed to invoke method", e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException(e.getCause());
+ }
+ }
+
+ /**
+ * Re-registers the broadcast receiver for any new messages that we want to listen for.
+ */
+ private void registerReceiverForInterprocessEvents(Context context) {
+ if (DEBUG_TRACE_ALL) {
+ logWithPid("registerReceiverForInterprocessEvents()");
+ }
+ // Rebuild the receiver filter with the new interprocess events
+ IntentFilter filter = new IntentFilter();
+ for (String eventName : mInterprocessEventNameMap.keySet()) {
+ filter.addAction(eventName);
+ if (DEBUG_TRACE_ALL) {
+ logWithPid(" filter: " + eventName);
+ }
+ }
+ // Re-register the receiver with the new filter
+ if (mHasRegisteredReceiver) {
+ context.unregisterReceiver(this);
+ }
+ context.registerReceiverAsUser(this, UserHandle.ALL, filter, PERMISSION_SELF, mHandler);
+ mHasRegisteredReceiver = true;
+ }
+
+ /**
+ * Returns whether this subscriber is currently registered. If {@param removeFoundSubscriber}
+ * is true, then remove the subscriber before returning.
+ */
+ private boolean findRegisteredSubscriber(Object subscriber, boolean removeFoundSubscriber) {
+ for (int i = mSubscribers.size() - 1; i >= 0; i--) {
+ Subscriber sub = mSubscribers.get(i);
+ if (sub.getReference() == subscriber) {
+ if (removeFoundSubscriber) {
+ mSubscribers.remove(i);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return whether {@param method} is a valid (normal or interprocess) event bus handler method
+ */
+ private boolean isValidEventBusHandlerMethod(Method method, Class<?>[] parameterTypes,
+ MutableBoolean isInterprocessEventOut) {
+ int modifiers = method.getModifiers();
+ if (Modifier.isPublic(modifiers) &&
+ Modifier.isFinal(modifiers) &&
+ method.getReturnType().equals(Void.TYPE) &&
+ parameterTypes.length == 1) {
+ if (EventBus.InterprocessEvent.class.isAssignableFrom(parameterTypes[0]) &&
+ method.getName().startsWith(INTERPROCESS_METHOD_PREFIX)) {
+ isInterprocessEventOut.value = true;
+ return true;
+ } else if (EventBus.Event.class.isAssignableFrom(parameterTypes[0]) &&
+ method.getName().startsWith(METHOD_PREFIX)) {
+ isInterprocessEventOut.value = false;
+ return true;
+ } else {
+ if (DEBUG_TRACE_ALL) {
+ if (!EventBus.Event.class.isAssignableFrom(parameterTypes[0])) {
+ logWithPid(" Expected method take an Event-based parameter: " + method.getName());
+ } else if (!method.getName().startsWith(INTERPROCESS_METHOD_PREFIX) &&
+ !method.getName().startsWith(METHOD_PREFIX)) {
+ logWithPid(" Expected method start with method prefix: " + method.getName());
+ }
+ }
+ }
+ } else {
+ if (DEBUG_TRACE_ALL) {
+ if (!Modifier.isPublic(modifiers)) {
+ logWithPid(" Expected method to be public: " + method.getName());
+ } else if (!Modifier.isFinal(modifiers)) {
+ logWithPid(" Expected method to be final: " + method.getName());
+ } else if (!method.getReturnType().equals(Void.TYPE)) {
+ logWithPid(" Expected method to return null: " + method.getName());
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Sorts the event handlers by priority and registration time.
+ */
+ private void sortEventHandlersByPriority(List<EventHandler> eventHandlers) {
+ Collections.sort(eventHandlers, EVENT_HANDLER_COMPARATOR);
+ }
+
+ /**
+ * Helper method to log the given {@param text} with the current process and user id.
+ */
+ private static void logWithPid(String text) {
+ Log.d(TAG, "[" + android.os.Process.myPid() + ", u" + UserHandle.myUserId() + "] " + text);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java
new file mode 100644
index 000000000000..52cfe18a9e26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppWidgetProviderChangedEvent.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import com.android.systemui.recents.RecentsAppWidgetHost;
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent by the {@link RecentsAppWidgetHost} whenever the search provider widget changes, and
+ * subscribers can update accordingly.
+ */
+public class AppWidgetProviderChangedEvent extends EventBus.Event {
+ // Simple event
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
new file mode 100644
index 000000000000..3b68574c2830
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/PackagesChangedEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.activity;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.RecentsPackageMonitor;
+import com.android.systemui.recents.views.TaskStackView;
+
+/**
+ * This event is sent by {@link RecentsPackageMonitor} when a package on the the system changes.
+ * {@link TaskStackView}s listen for this event, and remove the tasks associated with the removed
+ * packages.
+ */
+public class PackagesChangedEvent extends EventBus.Event {
+
+ public final RecentsPackageMonitor monitor;
+ public final String packageName;
+ public final int userId;
+
+ public PackagesChangedEvent(RecentsPackageMonitor monitor, String packageName, int userId) {
+ this.monitor = monitor;
+ this.packageName = packageName;
+ this.userId = userId;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java
new file mode 100644
index 000000000000..12e5d3d61cf7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/DismissTaskEvent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.TaskView;
+
+/**
+ * This is sent when a {@link TaskView} has been dismissed.
+ */
+public class DismissTaskEvent extends EventBus.Event {
+
+ public final Task task;
+ public final TaskView taskView;
+
+ public DismissTaskEvent(Task task, TaskView taskView) {
+ this.task = task;
+ this.taskView = taskView;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResizeTaskEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResizeTaskEvent.java
new file mode 100644
index 000000000000..e0d83fd847bf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ResizeTaskEvent.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+
+/**
+ * This is sent when a {@link Task} is resized.
+ */
+public class ResizeTaskEvent extends EventBus.Event {
+
+ public final Task task;
+
+ public ResizeTaskEvent(Task task) {
+ this.task = task;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java
new file mode 100644
index 000000000000..40c30b884eae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/ShowApplicationInfoEvent.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.ui;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+
+/**
+ * This is sent when a user wants to show the application info for a {@link Task}.
+ */
+public class ShowApplicationInfoEvent extends EventBus.Event {
+
+ public final Task task;
+
+ public ShowApplicationInfoEvent(Task task) {
+ this.task = task;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java
new file mode 100644
index 000000000000..f2c3c338383e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragDockStateChangedEvent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.ui.dragndrop;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+
+/**
+ * This event is sent when a user drag enters or exits a dock region.
+ */
+public class DragDockStateChangedEvent extends EventBus.Event {
+
+ public final Task task;
+ public final TaskStack.DockState dockState;
+
+ public DragDockStateChangedEvent(Task task, TaskStack.DockState dockState) {
+ this.task = task;
+ this.dockState = dockState;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
new file mode 100644
index 000000000000..827998d8529f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragEndEvent.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.ui.dragndrop;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.views.DragView;
+import com.android.systemui.recents.views.TaskView;
+
+/**
+ * This event is sent whenever a drag ends.
+ */
+public class DragEndEvent extends EventBus.Event {
+
+ public final Task task;
+ public final TaskView taskView;
+ public final DragView dragView;
+ public final TaskStack.DockState dockState;
+ public final ReferenceCountedTrigger postAnimationTrigger;
+
+ public DragEndEvent(Task task, TaskView taskView, DragView dragView,
+ TaskStack.DockState dockState, ReferenceCountedTrigger postAnimationTrigger) {
+ this.task = task;
+ this.taskView = taskView;
+ this.dragView = dragView;
+ this.dockState = dockState;
+ this.postAnimationTrigger = postAnimationTrigger;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
new file mode 100644
index 000000000000..2d42a0e0103d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/ui/dragndrop/DragStartEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.ui.dragndrop;
+
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.views.DragView;
+import com.android.systemui.recents.views.TaskView;
+
+/**
+ * This event is sent whenever a drag starts.
+ */
+public class DragStartEvent extends EventBus.Event {
+
+ public final Task task;
+ public final TaskView taskView;
+ public final DragView dragView;
+
+ public DragStartEvent(Task task, TaskView taskView, DragView dragView) {
+ this.task = task;
+ this.taskView = taskView;
+ this.dragView = dragView;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index b6d25f507cb9..568d2b1e3f2e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -16,6 +16,9 @@
package com.android.systemui.recents.misc;
+import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.INVALID_STACK_ID;
+
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
@@ -55,10 +58,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import android.util.MutableBoolean;
-import android.util.MutableFloat;
-import android.util.MutableInt;
import android.util.Pair;
-import android.util.Size;
import android.view.Display;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -300,7 +300,7 @@ public class SystemServicesProxy {
if (mIam == null) return;
try {
- mIam.moveTaskToDockedStack(taskId, createMode, true);
+ mIam.startActivityFromRecents(taskId, DOCKED_STACK_ID, null);
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -330,6 +330,21 @@ public class SystemServicesProxy {
return mAm.isInHomeStack(taskId);
}
+ /**
+ * @return whether there are any docked tasks.
+ */
+ public boolean hasDockedTask() {
+ if (mIam == null) return false;
+
+ ActivityManager.StackInfo stackInfo = null;
+ try {
+ stackInfo = mIam.getStackInfo(DOCKED_STACK_ID);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ return stackInfo != null;
+ }
+
/** Returns the top task thumbnail for the given task id */
public Bitmap getTaskThumbnail(int taskId) {
if (mAm == null) return null;
@@ -710,7 +725,8 @@ public class SystemServicesProxy {
ActivityOptions options) {
if (mIam != null) {
try {
- mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle());
+ mIam.startActivityFromRecents(
+ taskId, INVALID_STACK_ID, options == null ? null : options.toBundle());
return true;
} catch (Exception e) {
Console.logError(context,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index e810410cd8bd..93c5ee7c873e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -21,12 +21,29 @@ import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.View;
+import android.view.ViewParent;
import java.util.ArrayList;
/* Common code */
public class Utilities {
+ /**
+ * @return the first parent walking up the view hierarchy that has the given class type.
+ *
+ * @param parentClass must be a class derived from {@link View}
+ */
+ public static <T extends View> T findParent(View v, Class<T> parentClass) {
+ ViewParent parent = v.getParent();
+ while (parent != null) {
+ if (parent.getClass().equals(parentClass)) {
+ return (T) parent;
+ }
+ parent = parent.getParent();
+ }
+ return null;
+ }
+
/** Scales a rect about its centroid */
public static void scaleRectAboutCenter(Rect r, float scale) {
if (scale != 1.0f) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
index e48e5f053f5f..8f9a2935c3a6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsPackageMonitor.java
@@ -21,6 +21,8 @@ import android.content.Context;
import android.os.Looper;
import android.os.UserHandle;
import com.android.internal.content.PackageMonitor;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.PackagesChangedEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import java.util.HashSet;
@@ -31,18 +33,9 @@ import java.util.List;
* Recents list.
*/
public class RecentsPackageMonitor extends PackageMonitor {
- public interface PackageCallbacks {
- public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName,
- int userId);
- }
-
- PackageCallbacks mCb;
- SystemServicesProxy mSystemServicesProxy;
/** Registers the broadcast receivers with the specified callbacks. */
- public void register(Context context, PackageCallbacks cb) {
- mSystemServicesProxy = new SystemServicesProxy(context);
- mCb = cb;
+ public void register(Context context) {
try {
// We register for events from all users, but will cross-reference them with
// packages for the current user and any profiles they have
@@ -60,17 +53,13 @@ public class RecentsPackageMonitor extends PackageMonitor {
} catch (IllegalStateException e) {
e.printStackTrace();
}
- mSystemServicesProxy = null;
- mCb = null;
}
@Override
public void onPackageRemoved(String packageName, int uid) {
- if (mCb == null) return;
-
// Notify callbacks that a package has changed
final int eventUserId = getChangingUserId();
- mCb.onPackagesChanged(this, packageName, eventUserId);
+ EventBus.getDefault().send(new PackagesChangedEvent(this, packageName, eventUserId));
}
@Override
@@ -81,11 +70,9 @@ public class RecentsPackageMonitor extends PackageMonitor {
@Override
public void onPackageModified(String packageName) {
- if (mCb == null) return;
-
// Notify callbacks that a package has changed
final int eventUserId = getChangingUserId();
- mCb.onPackagesChanged(this, packageName, eventUserId);
+ EventBus.getDefault().send(new PackagesChangedEvent(this, packageName, eventUserId));
}
/**
@@ -108,7 +95,8 @@ public class RecentsPackageMonitor extends PackageMonitor {
// If we know that the component still exists in the package, then skip
continue;
}
- if (mSystemServicesProxy.getActivityInfo(cn, userId) != null) {
+ SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+ if (ssp.getActivityInfo(cn, userId) != null) {
existingComponents.add(cn);
} else {
removedComponents.add(cn);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index 760382ed469e..39bef81386b4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -262,8 +262,6 @@ public class RecentsTaskLoader {
TaskResourceLoadQueue mLoadQueue;
TaskResourceLoader mLoader;
- RecentsPackageMonitor mPackageMonitor;
-
int mMaxThumbnailCacheSize;
int mMaxIconCacheSize;
int mNumVisibleTasksLoaded;
@@ -293,7 +291,6 @@ public class RecentsTaskLoader {
// Initialize the proxy, cache and loaders
mSystemServicesProxy = new SystemServicesProxy(context);
- mPackageMonitor = new RecentsPackageMonitor();
mLoadQueue = new TaskResourceLoadQueue();
mApplicationIconCache = new DrawableLruCache(iconCacheSize);
mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
@@ -519,17 +516,6 @@ public class RecentsTaskLoader {
mLoadQueue.clearTasks();
}
- /** Registers any broadcast receivers. */
- public void registerReceivers(Context context, RecentsPackageMonitor.PackageCallbacks cb) {
- // Register the broadcast receiver to handle messages related to packages being added/removed
- mPackageMonitor.register(context, cb);
- }
-
- /** Unregisters any broadcast receivers. */
- public void unregisterReceivers() {
- mPackageMonitor.unregister();
- }
-
/**
* Handles signals from the system, trimming memory when requested to prevent us from running
* out of memory.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 20d9203ef71f..0fb235b9e23a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -16,12 +16,15 @@
package com.android.systemui.recents.model;
+import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.misc.NamedCounter;
import com.android.systemui.recents.misc.Utilities;
-import com.android.systemui.R;
import java.util.ArrayList;
import java.util.Collections;
@@ -30,6 +33,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Random;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
+import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+
/**
* An interface for a task filter to query whether a particular task should show in a stack.
@@ -174,6 +180,63 @@ public class TaskStack {
public void onStackUnfiltered(TaskStack newStack, ArrayList<Task> curTasks);
}
+
+ public enum DockState {
+ LEFT(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
+ new RectF(0, 0, 0.25f, 1), new RectF(0, 0, 0.5f, 1), new RectF(0.5f, 0, 1, 1)),
+ TOP(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
+ new RectF(0, 0, 1, 0.25f), new RectF(0, 0, 1, 0.5f), new RectF(0, 0.5f, 1, 1)),
+ RIGHT(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
+ new RectF(0.75f, 0, 1, 1), new RectF(0.5f, 0, 1, 1), new RectF(0, 0, 0.5f, 1)),
+ BOTTOM(DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
+ new RectF(0, 0.75f, 1, 1), new RectF(0, 0.5f, 1, 1), new RectF(0, 0, 1, 0.5f));
+
+ public final int createMode;
+ private final RectF touchArea;
+ private final RectF dockArea;
+ private final RectF stackArea;
+
+ /**
+ * @param createMode used to pass to ActivityManager to dock the task
+ * @param touchArea the area in which touch will initiate this dock state
+ * @param stackArea the area for the stack if a task is docked
+ */
+ DockState(int createMode, RectF touchArea, RectF dockArea, RectF stackArea) {
+ this.createMode = createMode;
+ this.touchArea = touchArea;
+ this.dockArea = dockArea;
+ this.stackArea = stackArea;
+ }
+
+ /**
+ * Returns whether {@param x} and {@param y} are contained in the touch area scaled to the
+ * given {@param width} and {@param height}.
+ */
+ public boolean touchAreaContainsPoint(int width, int height, float x, float y) {
+ int left = (int) (touchArea.left * width);
+ int top = (int) (touchArea.top * height);
+ int right = (int) (touchArea.right * width);
+ int bottom = (int) (touchArea.bottom * height);
+ return x >= left && y >= top && x <= right && y <= bottom;
+ }
+
+ /**
+ * Returns the docked task bounds with the given {@param width} and {@param height}.
+ */
+ public Rect getDockedBounds(int width, int height) {
+ return new Rect((int) (dockArea.left * width), (int) (dockArea.top * height),
+ (int) (dockArea.right * width), (int) (dockArea.bottom * height));
+ }
+
+ /**
+ * Returns the stack bounds with the given {@param width} and {@param height}.
+ */
+ public Rect getStackBounds(int width, int height) {
+ return new Rect((int) (stackArea.left * width), (int) (stackArea.top * height),
+ (int) (stackArea.right * width), (int) (stackArea.bottom * height));
+ }
+ }
+
// The task offset to apply to a task id as a group affiliation
static final int IndividualTaskIdOffset = 1 << 16;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java b/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java
new file mode 100644
index 000000000000..96dfaac360c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/DragView.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.widget.ImageView;
+
+public class DragView extends ImageView {
+
+ // The offset from the top-left of the dragBitmap
+ Point mTopLeftOffset;
+
+ public DragView(Context context, Bitmap dragBitmap, Point tlOffset) {
+ super(context);
+
+ mTopLeftOffset = tlOffset;
+ setImageBitmap(dragBitmap);
+ }
+
+ public Point getTopLeftOffset() {
+ return mTopLeftOffset;
+ }
+}
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 73c9be94eeae..e4061553e631 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -18,36 +18,39 @@ package com.android.systemui.recents.views;
import android.app.ActivityManager;
import android.app.ActivityOptions;
-import android.app.TaskStackBuilder;
import android.content.Context;
-import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.net.Uri;
+import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.AppTransitionAnimationSpec;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManagerGlobal;
+import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsAppWidgetHostView;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
-import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
@@ -59,8 +62,7 @@ import java.util.List;
* This view is the the top level layout that contains TaskStacks (which are laid out according
* to their SpaceNode bounds.
*/
-public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks,
- RecentsPackageMonitor.PackageCallbacks {
+public class RecentsView extends FrameLayout implements TaskStackView.TaskStackViewCallbacks {
private static final String TAG = "RecentsView";
@@ -73,7 +75,6 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
public void onAllTaskViewsDismissed();
public void onExitToHomeAnimationTriggered();
public void onScreenPinningRequest();
- public void onTaskResize(Task t);
public void runAfterPause(Runnable r);
}
@@ -84,6 +85,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
TaskStackView mTaskStackView;
RecentsAppWidgetHostView mSearchBar;
RecentsViewCallbacks mCb;
+
+ RecentsViewTouchHandler mTouchHandler;
+ DragView mDragView;
+ ColorDrawable mDockRegionOverlay;
+
Interpolator mFastOutSlowInInterpolator;
Rect mSystemInsets = new Rect();
@@ -102,10 +108,14 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+ setWillNotDraw(false);
mConfig = RecentsConfiguration.getInstance();
mInflater = LayoutInflater.from(context);
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
com.android.internal.R.interpolator.fast_out_slow_in);
+ mTouchHandler = new RecentsViewTouchHandler(this);
+ mDockRegionOverlay = new ColorDrawable(0x66000000);
+ mDockRegionOverlay.setAlpha(0);
}
/** Sets the callbacks */
@@ -206,6 +216,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
ArrayList<Task> tasks = stack.getTasks();
// Find the launch task in the stack
+ // TODO: replace this with an event from RecentsActivity
if (!tasks.isEmpty()) {
int taskCount = tasks.size();
for (int j = 0; j < taskCount; j++) {
@@ -273,6 +284,20 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
}
+ @Override
+ protected void onAttachedToWindow() {
+ EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+ EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+ super.onAttachedToWindow();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ EventBus.getDefault().unregister(this);
+ EventBus.getDefault().unregister(mTouchHandler);
+ }
+
/**
* This is called with the full size of the window since we are handling our own insets.
*/
@@ -299,6 +324,12 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec);
}
+ if (mDragView != null) {
+ mDragView.measure(
+ MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
+ }
+
setMeasuredDimension(width, height);
}
@@ -320,6 +351,11 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) {
mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
}
+
+ if (mDragView != null) {
+ mDragView.layout(left, top, left + mDragView.getMeasuredWidth(),
+ top + mDragView.getMeasuredHeight());
+ }
}
@Override
@@ -329,6 +365,24 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
return insets.consumeSystemWindowInsets();
}
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return mTouchHandler.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ return mTouchHandler.onTouchEvent(ev);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (mDockRegionOverlay.getAlpha() > 0) {
+ mDockRegionOverlay.draw(canvas);
+ }
+ }
+
/** Notifies each task view of the user interaction. */
public void onUserInteraction() {
// Get the first stack view
@@ -611,7 +665,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
} else {
// Dismiss the task and return the user to home if we fail to
// launch the task
- onTaskViewDismissed(task);
+ EventBus.getDefault().send(new DismissTaskEvent(task, tv));
if (mCb != null) {
mCb.onTaskLaunchFailed();
}
@@ -648,37 +702,15 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
@Override
- public void onTaskViewAppInfoClicked(Task t) {
- // Create a new task stack with the application info details activity
- Intent baseIntent = t.key.baseIntent;
- Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
- Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
- intent.setComponent(intent.resolveActivity(getContext().getPackageManager()));
- TaskStackBuilder.create(getContext())
- .addNextIntentWithParentStack(intent).startActivities(null,
- new UserHandle(t.key.userId));
- }
-
- @Override
- public void onTaskViewDismissed(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 onTaskViewDismissed(), 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
- loader.getSystemServicesProxy().removeTask(t.key.id);
- }
-
- @Override
public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks) {
+ /* TODO: Not currently enabled
if (removedTasks != null) {
int taskCount = removedTasks.size();
for (int i = 0; i < taskCount; i++) {
onTaskViewDismissed(removedTasks.get(i));
}
}
+ */
mCb.onAllTaskViewsDismissed();
@@ -726,20 +758,63 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV
}
}
- @Override
- public void onTaskResize(Task t) {
- if (mCb != null) {
- mCb.onTaskResize(t);
- }
+ /**** EventBus Events ****/
+
+ public final void onBusEvent(DragStartEvent event) {
+ // Add the drag view
+ mDragView = event.dragView;
+ addView(mDragView);
}
- /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
+ public final void onBusEvent(DragDockStateChangedEvent event) {
+ // Update the task stack positions, and then
+ if (event.dockState != null) {
+ // Draw an overlay on the bounds of the dock task
+ mDockRegionOverlay.setBounds(
+ event.dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight()));
+ mDockRegionOverlay.setAlpha(255);
+ } else {
+ mDockRegionOverlay.setAlpha(0);
+ }
+ invalidate();
+ }
- @Override
- public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
- // Propagate this event down to each task stack view
- if (mTaskStackView != null) {
- mTaskStackView.onPackagesChanged(monitor, packageName, userId);
+ public final void onBusEvent(final DragEndEvent event) {
+ event.postAnimationTrigger.increment();
+ event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ // Remove the drag view
+ removeView(mDragView);
+ mDragView = null;
+ mDockRegionOverlay.setAlpha(0);
+ invalidate();
+
+ // Dock the new task if we are hovering over a valid dock state
+ if (event.dockState != null) {
+ SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+ ssp.setTaskResizeable(event.task.key.id);
+ ssp.dockTask(event.task.key.id, event.dockState.createMode);
+ launchTask(event.task, null);
+ }
+ }
+ });
+ if (event.dockState == null) {
+ // Animate the alpha back to what it was before
+ Rect taskBounds = mTaskStackView.getStackAlgorithm().getUntransformedTaskViewBounds();
+ int left = taskBounds.left + (int) ((1f - event.taskView.getScaleX()) * taskBounds.width()) / 2;
+ int top = taskBounds.top + (int) ((1f - event.taskView.getScaleY()) * taskBounds.height()) / 2;
+ event.dragView.animate()
+ .alpha(1f)
+ .translationX(left + event.taskView.getTranslationX())
+ .translationY(top + event.taskView.getTranslationY())
+ .setDuration(175)
+ .setInterpolator(new AccelerateInterpolator(1.5f))
+ .withEndAction(event.postAnimationTrigger.decrementAsRunnable())
+ .withLayer()
+ .start();
+ } else {
+ event.postAnimationTrigger.decrement();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
new file mode 100644
index 000000000000..8dea0cd1ab38
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsViewTouchHandler.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.views;
+
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.view.MotionEvent;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.dragndrop.DragDockStateChangedEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.misc.ReferenceCountedTrigger;
+import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.model.TaskStack;
+
+
+/**
+ * Represents the dock regions for each orientation.
+ */
+class DockRegion {
+ public static TaskStack.DockState[] LANDSCAPE = {
+ TaskStack.DockState.LEFT, TaskStack.DockState.RIGHT
+ };
+ public static TaskStack.DockState[] PORTRAIT = {
+ TaskStack.DockState.TOP, TaskStack.DockState.BOTTOM
+ };
+}
+
+/**
+ * Handles touch events for a RecentsView.
+ */
+class RecentsViewTouchHandler {
+
+ private RecentsView mRv;
+
+ private Task mDragTask;
+ private TaskView mTaskView;
+ private DragView mDragView;
+
+ private Point mDownPos = new Point();
+ private boolean mDragging;
+ private TaskStack.DockState mLastDockState;
+
+ public RecentsViewTouchHandler(RecentsView rv) {
+ mRv = rv;
+ }
+
+ /** Touch preprocessing for handling below */
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN:
+ mDownPos.set((int) ev.getX(), (int) ev.getY());
+ break;
+ }
+ return mDragging;
+ }
+
+ /** Handles touch events once we have intercepted them */
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (!mDragging) return false;
+
+ boolean isLandscape = mRv.getResources().getConfiguration().orientation ==
+ Configuration.ORIENTATION_LANDSCAPE;
+ int action = ev.getAction();
+ switch (action & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN:
+ mDownPos.set((int) ev.getX(), (int) ev.getY());
+ break;
+ case MotionEvent.ACTION_MOVE: {
+ int width = mRv.getMeasuredWidth();
+ int height = mRv.getMeasuredHeight();
+ float evX = ev.getX();
+ float evY = ev.getY();
+ float x = evX - mDragView.getTopLeftOffset().x;
+ float y = evY - mDragView.getTopLeftOffset().y;
+
+ // Update the dock state
+ TaskStack.DockState[] dockStates = isLandscape ?
+ DockRegion.LANDSCAPE : DockRegion.PORTRAIT;
+ TaskStack.DockState foundDockState = null;
+ for (int i = 0; i < dockStates.length; i++) {
+ TaskStack.DockState state = dockStates[i];
+ if (state.touchAreaContainsPoint(width, height, evX, evY)) {
+ foundDockState = state;
+ break;
+ }
+ }
+ if (mLastDockState != foundDockState) {
+ mLastDockState = foundDockState;
+ EventBus.getDefault().send(new DragDockStateChangedEvent(mDragTask,
+ foundDockState));
+ }
+
+ mDragView.setTranslationX(x);
+ mDragView.setTranslationY(y);
+ break;
+ }
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL: {
+ ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(
+ mRv.getContext(), null, null, null);
+ postAnimationTrigger.increment();
+ EventBus.getDefault().send(new DragEndEvent(mDragTask, mTaskView, mDragView,
+ mLastDockState, postAnimationTrigger));
+ postAnimationTrigger.decrement();
+ break;
+ }
+ }
+ return true;
+ }
+
+ /**** Events ****/
+
+ public final void onBusEvent(DragStartEvent event) {
+ mRv.getParent().requestDisallowInterceptTouchEvent(true);
+ mDragging = true;
+ mDragTask = event.task;
+ mTaskView = event.taskView;
+ mDragView = event.dragView;
+
+ float x = mDownPos.x - mDragView.getTopLeftOffset().x;
+ float y = mDownPos.y - mDragView.getTopLeftOffset().y;
+ mDragView.setTranslationX(x);
+ mDragView.setTranslationY(y);
+ }
+
+ public final void onBusEvent(DragEndEvent event) {
+ mDragging = false;
+ mDragTask = null;
+ mTaskView = null;
+ mDragView = null;
+ mLastDockState = null;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
index 7ce50d803ee2..b28cc21737e4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -22,7 +22,6 @@ import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.systemui.R;
-import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
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 b5f29a01c2b0..78b986263213 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -29,15 +29,17 @@ import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
-import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.activity.PackagesChangedEvent;
+import com.android.systemui.recents.events.ui.DismissTaskEvent;
import com.android.systemui.recents.misc.DozeTrigger;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
-import com.android.systemui.recents.model.RecentsPackageMonitor;
import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.recents.model.TaskStack;
@@ -54,19 +56,15 @@ import java.util.List;
/* The visual representation of a task stack view */
public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCallbacks,
TaskView.TaskViewCallbacks, TaskStackViewScroller.TaskStackViewScrollerCallbacks,
- ViewPool.ViewPoolConsumer<TaskView, Task>, RecentsPackageMonitor.PackageCallbacks {
+ ViewPool.ViewPoolConsumer<TaskView, Task> {
/** The TaskView callbacks */
interface TaskStackViewCallbacks {
public void onTaskViewClicked(TaskStackView stackView, TaskView tv, TaskStack stack, Task t,
boolean lockToTask, boolean boundsValid, Rect bounds);
- public void onTaskViewAppInfoClicked(Task t);
- public void onTaskViewDismissed(Task t);
public void onAllTaskViewsDismissed(ArrayList<Task> removedTasks);
public void onTaskStackFilterTriggered();
public void onTaskStackUnfilterTriggered();
-
- public void onTaskResize(Task t);
}
RecentsConfiguration mConfig;
@@ -77,7 +75,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
TaskStackViewTouchHandler mTouchHandler;
TaskStackViewCallbacks mCb;
ViewPool<TaskView, Task> mViewPool;
- ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<TaskViewTransform>();
+ ArrayList<TaskViewTransform> mCurrentTaskTransforms = new ArrayList<>();
DozeTrigger mUIDozeTrigger;
DismissView mDismissAllButton;
boolean mDismissAllButtonAnimating;
@@ -97,9 +95,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
Matrix mTmpMatrix = new Matrix();
Rect mTmpRect = new Rect();
TaskViewTransform mTmpTransform = new TaskViewTransform();
- HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<Task, TaskView>();
- ArrayList<TaskView> mTaskViews = new ArrayList<TaskView>();
- List<TaskView> mImmutableTaskViews = new ArrayList<TaskView>();
+ HashMap<Task, TaskView> mTmpTaskViewMap = new HashMap<>();
+ ArrayList<TaskView> mTaskViews = new ArrayList<>();
+ List<TaskView> mImmutableTaskViews = new ArrayList<>();
LayoutInflater mInflater;
boolean mLayersDisabled;
@@ -147,6 +145,18 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
mCb = cb;
}
+ @Override
+ protected void onAttachedToWindow() {
+ EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+ super.onAttachedToWindow();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ EventBus.getDefault().unregister(this);
+ }
+
/** Sets the task stack */
void setStack(TaskStack stack) {
// Set the new stack
@@ -1161,11 +1171,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
// Fade the dismiss button back in
showDismissAllButton();
}
-
- // Notify the callback that we've removed the task and it can clean up after it. Note, we
- // do this after onAllTaskViewsDismissed() is called, to allow the home activity to be
- // started before the call to remove the task.
- mCb.onTaskViewDismissed(removedTask);
}
@Override
@@ -1291,9 +1296,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
RecentsTaskLoader.getInstance().loadTaskData(task);
// If the doze trigger has already fired, then update the state for this task view
- if (mUIDozeTrigger.hasTriggered()) {
- tv.setNoUserInteractionState();
- }
+ tv.setNoUserInteractionState();
// If we've finished the start animation, then ensure we always enable the focus animations
if (mStartEnterAnimationCompleted) {
@@ -1343,27 +1346,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
/**** TaskViewCallbacks Implementation ****/
@Override
- public void onTaskViewAppIconClicked(TaskView tv) {
- if (Constants.DebugFlags.App.EnableTaskFiltering) {
- if (mStack.hasFilteredTasks()) {
- mStack.unfilterTasks();
- } else {
- mStack.filterTasks(tv.getTask());
- }
- }
- }
-
- @Override
- public void onTaskViewAppInfoClicked(TaskView tv) {
- if (mCb != null) {
- mCb.onTaskViewAppInfoClicked(tv.getTask());
-
- // Keep track of app-info invocations
- MetricsLogger.count(getContext(), "overview_app_info", 1);
- }
- }
-
- @Override
public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask) {
// Cancel any doze triggers
mUIDozeTrigger.stopDozing();
@@ -1374,33 +1356,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
@Override
- public void onTaskViewDismissed(TaskView tv) {
- Task task = tv.getTask();
- int taskIndex = mStack.indexOfTask(task);
- boolean taskWasFocused = tv.isFocusedTask();
- // Announce for accessibility
- tv.announceForAccessibility(getContext().getString(R.string.accessibility_recents_item_dismissed,
- tv.getTask().activityLabel));
- // Remove the task from the view
- mStack.removeTask(task);
- // If the dismissed task was focused, then we should focus the new task in the same index
- if (taskWasFocused) {
- ArrayList<Task> tasks = mStack.getTasks();
- int nextTaskIndex = Math.min(tasks.size() - 1, taskIndex - 1);
- if (nextTaskIndex >= 0) {
- Task nextTask = tasks.get(nextTaskIndex);
- TaskView nextTv = getChildViewForTask(nextTask);
- if (nextTv != null) {
- // Focus the next task, and only animate the visible state if we are launched
- // from Alt-Tab
- RecentsActivityLaunchState launchState = mConfig.getLaunchState();
- nextTv.setFocusedTask(launchState.launchedWithAltTab);
- }
- }
- }
- }
-
- @Override
public void onTaskViewClipStateChanged(TaskView tv) {
if (!mStackViewsDirty) {
invalidate();
@@ -1414,13 +1369,6 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
- @Override
- public void onTaskResize(TaskView tv) {
- if (mCb != null) {
- mCb.onTaskResize(tv.getTask());
- }
- }
-
/**** TaskStackViewScroller.TaskStackViewScrollerCallbacks ****/
@Override
@@ -1430,13 +1378,12 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
postInvalidateOnAnimation();
}
- /**** RecentsPackageMonitor.PackageCallbacks Implementation ****/
+ /**** EventBus Events ****/
- @Override
- public void onPackagesChanged(RecentsPackageMonitor monitor, String packageName, int userId) {
+ public final void onBusEvent(PackagesChangedEvent event) {
// Compute which components need to be removed
- HashSet<ComponentName> removedComponents = monitor.computeComponentsRemoved(
- mStack.getTaskKeys(), packageName, userId);
+ HashSet<ComponentName> removedComponents = event.monitor.computeComponentsRemoved(
+ mStack.getTaskKeys(), event.packageName, event.userId);
// For other tasks, just remove them directly if they no longer exist
ArrayList<Task> tasks = mStack.getTasks();
@@ -1459,4 +1406,33 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal
}
}
}
+
+ public final void onBusEvent(DismissTaskEvent event) {
+ TaskView tv = event.taskView;
+ Task task = tv.getTask();
+ int taskIndex = mStack.indexOfTask(task);
+ boolean taskWasFocused = tv.isFocusedTask();
+
+ // Announce for accessibility
+ tv.announceForAccessibility(getContext().getString(R.string.accessibility_recents_item_dismissed,
+ tv.getTask().activityLabel));
+
+ // Remove the task from the view
+ mStack.removeTask(task);
+ // If the dismissed task was focused, then we should focus the new task in the same index
+ if (taskWasFocused) {
+ ArrayList<Task> tasks = mStack.getTasks();
+ int nextTaskIndex = Math.min(tasks.size() - 1, taskIndex - 1);
+ if (nextTaskIndex >= 0) {
+ Task nextTask = tasks.get(nextTaskIndex);
+ TaskView nextTv = getChildViewForTask(nextTask);
+ if (nextTv != null) {
+ // Focus the next task, and only animate the visible state if we are launched
+ // from Alt-Tab
+ RecentsActivityLaunchState launchState = mConfig.getLaunchState();
+ nextTv.setFocusedTask(launchState.launchedWithAltTab);
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
index e9f6a46c9707..a32b2422aa24 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java
@@ -16,9 +16,9 @@
package com.android.systemui.recents.views;
+import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.model.Task;
-import com.android.systemui.R;
import java.util.ArrayList;
import java.util.HashMap;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index 7f4c0a5f7cb4..a8e6f4703177 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -18,11 +18,11 @@ package com.android.systemui.recents.views;
import android.content.Context;
import android.graphics.Rect;
+import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
-import com.android.systemui.R;
import java.util.ArrayList;
import java.util.HashMap;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index f0ae87f6f9ae..e4fbc76f9d3b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -24,8 +24,8 @@ import android.content.Context;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.OverScroller;
-import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.R;
+import com.android.systemui.recents.misc.Utilities;
/* The scrolling logic for a TaskStackView */
public class TaskStackViewScroller {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
index 86eced8a763d..48c5b46c34d7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -24,9 +24,11 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;
import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
-import com.android.systemui.R;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.DismissTaskEvent;
import java.util.List;
@@ -435,7 +437,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback {
// Re-enable touch events from this task view
tv.setTouchEnabled(true);
// Remove the task view from the stack
- mSv.onTaskViewDismissed(tv);
+ EventBus.getDefault().send(new DismissTaskEvent(tv.getTask(), tv));
// Keep track of deletions by keyboard
MetricsLogger.histogram(tv.getContext(), "overview_task_dismissed_source",
Constants.Metrics.DismissSourceSwipeGesture);
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 bbbaccf3fa5f..9d08ee907626 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -21,26 +21,35 @@ import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
+import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewOutlineProvider;
-import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
-import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
+import com.android.systemui.recents.RecentsActivity;
import com.android.systemui.recents.RecentsActivityLaunchState;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.DismissTaskEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
+import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.RecentsTaskLoader;
import com.android.systemui.recents.model.Task;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -50,14 +59,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
/** The TaskView callbacks */
interface TaskViewCallbacks {
- public void onTaskViewAppIconClicked(TaskView tv);
- public void onTaskViewAppInfoClicked(TaskView tv);
public void onTaskViewClicked(TaskView tv, Task task, boolean lockToTask);
- public void onTaskViewDismissed(TaskView tv);
public void onTaskViewClipStateChanged(TaskView tv);
public void onTaskViewFocusChanged(TaskView tv, boolean focused);
-
- public void onTaskResize(TaskView tv);
}
RecentsConfiguration mConfig;
@@ -84,6 +88,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
View mActionButtonView;
TaskViewCallbacks mCb;
+ Point mDownTouchPos = new Point();
+
Interpolator mFastOutSlowInInterpolator;
Interpolator mFastOutLinearInInterpolator;
Interpolator mQuintOutInterpolator;
@@ -174,6 +180,14 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
}
@Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mDownTouchPos.set((int) (ev.getX() * getScaleX()), (int) (ev.getY() * getScaleY()));
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
@@ -535,9 +549,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
startDeleteTaskAnimation(new Runnable() {
@Override
public void run() {
- if (mCb != null) {
- mCb.onTaskViewDismissed(tv);
- }
+ EventBus.getDefault().send(new DismissTaskEvent(mTask, tv));
}
}, 0);
}
@@ -615,8 +627,12 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
/** Compute the dim as a function of the scale of this view. */
int getDimFromTaskProgress() {
+ // TODO: Temporarily disable the dim on the stack
+ /*
float dim = mMaxDimScale * mDimInterpolator.getInterpolation(1f - mTaskProgress);
return (int) (dim * 255);
+ */
+ return 0;
}
/** Update the dim as a function of the scale of this view. */
@@ -725,17 +741,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
mThumbnailView.rebindToTask(mTask);
mHeaderView.rebindToTask(mTask);
// Rebind any listeners
- AccessibilityManager am = (AccessibilityManager) getContext().
- getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (Constants.DebugFlags.App.EnableTaskFiltering || (am != null && am.isEnabled())) {
- mHeaderView.mApplicationIcon.setOnClickListener(this);
- }
- mHeaderView.mDismissButton.setOnClickListener(this);
- if (mConfig.multiWindowEnabled) {
- mHeaderView.mMoveTaskButton.setOnClickListener(this);
- }
mActionButtonView.setOnClickListener(this);
- mHeaderView.mApplicationIcon.setOnLongClickListener(this);
+ setOnLongClickListener(mConfig.hasDockedTasks ? null : this);
}
mTaskDataLoaded = true;
}
@@ -748,13 +755,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
mThumbnailView.unbindFromTask();
mHeaderView.unbindFromTask();
// Unbind any listeners
- mHeaderView.mApplicationIcon.setOnClickListener(null);
- mHeaderView.mDismissButton.setOnClickListener(null);
- if (mConfig.multiWindowEnabled) {
- mHeaderView.mMoveTaskButton.setOnClickListener(null);
- }
mActionButtonView.setOnClickListener(null);
- mHeaderView.mApplicationIcon.setOnLongClickListener(null);
}
mTaskDataLoaded = false;
}
@@ -768,47 +769,12 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
@Override
public void onClick(final View v) {
- final TaskView tv = this;
- final boolean delayViewClick = (v != this) && (v != mActionButtonView);
- if (delayViewClick) {
- // We purposely post the handler delayed to allow for the touch feedback to draw
- postDelayed(new Runnable() {
- @Override
- public void run() {
- if (v == mHeaderView.mApplicationIcon) {
- if (Constants.DebugFlags.App.EnableTaskFiltering) {
- if (mCb != null) {
- mCb.onTaskViewAppIconClicked(tv);
- }
- } else {
- AccessibilityManager am = (AccessibilityManager) getContext().
- getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (am != null && am.isEnabled()) {
- if (mCb != null) {
- mCb.onTaskViewAppInfoClicked(tv);
- }
- }
- }
- } else if (v == mHeaderView.mDismissButton) {
- dismissTask();
- // Keep track of deletions by the dismiss button
- MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
- Constants.Metrics.DismissSourceHeaderButton);
- } else if (v == mHeaderView.mMoveTaskButton) {
- if (mCb != null) {
- mCb.onTaskResize(tv);
- }
- }
- }
- }, 125);
- } else {
- if (v == mActionButtonView) {
- // Reset the translation of the action button before we animate it out
- mActionButtonView.setTranslationZ(0f);
- }
- if (mCb != null) {
- mCb.onTaskViewClicked(tv, tv.getTask(), (v == mActionButtonView));
- }
+ if (v == mActionButtonView) {
+ // Reset the translation of the action button before we animate it out
+ mActionButtonView.setTranslationZ(0f);
+ }
+ if (mCb != null) {
+ mCb.onTaskViewClicked(this, mTask, (v == mActionButtonView));
}
}
@@ -816,12 +782,64 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks,
@Override
public boolean onLongClick(View v) {
- if (v == mHeaderView.mApplicationIcon) {
- if (mCb != null) {
- mCb.onTaskViewAppInfoClicked(this);
- return true;
- }
+ if (v == this) {
+ // Start listening for drag events
+ setClipViewInStack(false);
+
+ int width = (int) (getScaleX() * getWidth());
+ int height = (int) (getScaleY() * getHeight());
+ Bitmap dragBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(dragBitmap);
+ c.scale(getScaleX(), getScaleY());
+ mThumbnailView.draw(c);
+ mHeaderView.draw(c);
+ c.setBitmap(null);
+
+ // Initiate the drag
+ final DragView dragView = new DragView(getContext(), dragBitmap, mDownTouchPos);
+ dragView.setOutlineProvider(mViewBounds);
+ dragView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ // Hide this task view after the drag view is attached
+ setVisibility(View.INVISIBLE);
+ // Animate the alpha slightly to indicate dragging
+ dragView.setElevation(getElevation());
+ dragView.setTranslationZ(getTranslationZ());
+ dragView.animate()
+ .alpha(0.75f)
+ .setDuration(175)
+ .setInterpolator(new AccelerateInterpolator(1.5f))
+ .withLayer()
+ .start();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ }
+ });
+ EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
+ EventBus.getDefault().send(new DragStartEvent(mTask, this, dragView));
+ return true;
}
return false;
}
+
+ /**** Events ****/
+
+ public final void onBusEvent(DragEndEvent event) {
+ event.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
+ @Override
+ public void run() {
+ // If docked state == null:
+ // Animate the drag view back from where it is, to the view location, then after it returns,
+ // update the clip state
+ setClipViewInStack(true);
+
+ // Show this task view
+ setVisibility(View.VISIBLE);
+ }
+ });
+ EventBus.getDefault().unregister(this);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index f68dd64b0bf2..949d515a75f0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -39,14 +39,19 @@ import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
+import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.events.EventBus;
+import com.android.systemui.recents.events.ui.ResizeTaskEvent;
+import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.RecentsTaskLoader;
@@ -54,10 +59,12 @@ import com.android.systemui.recents.model.Task;
/* The task bar view */
-public class TaskViewHeader extends FrameLayout {
+public class TaskViewHeader extends FrameLayout
+ implements View.OnClickListener, View.OnLongClickListener {
RecentsConfiguration mConfig;
private SystemServicesProxy mSsp;
+ Task mTask;
// Header views
ImageView mMoveTaskButton;
@@ -144,15 +151,15 @@ public class TaskViewHeader extends FrameLayout {
protected void onFinishInflate() {
// Initialize the icon and description views
mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
+ mApplicationIcon.setOnLongClickListener(this);
mActivityDescription = (TextView) findViewById(R.id.activity_description);
mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
+ mDismissButton.setOnClickListener(this);
mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
// Hide the backgrounds if they are ripple drawables
- if (!Constants.DebugFlags.App.EnableTaskFiltering) {
- if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
- mApplicationIcon.setBackground(null);
- }
+ if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
+ mApplicationIcon.setBackground(null);
}
mBackgroundColorDrawable = (GradientDrawable) getContext().getDrawable(R.drawable
@@ -203,6 +210,8 @@ public class TaskViewHeader extends FrameLayout {
/** Binds the bar view to the task */
public void rebindToTask(Task t) {
+ mTask = t;
+
// If an activity icon is defined, then we use that as the primary icon to show in the bar,
// otherwise, we fall back to the application icon
if (t.activityIcon != null) {
@@ -235,12 +244,26 @@ public class TaskViewHeader extends FrameLayout {
mLightDismissDrawable : mDarkDismissDrawable);
mDismissButton.setContentDescription(String.format(mDismissContentDescription,
t.contentDescription));
- mMoveTaskButton.setVisibility((mConfig.multiWindowEnabled) ? View.VISIBLE : View.INVISIBLE);
- if (mConfig.multiWindowEnabled) {
- updateResizeTaskBarIcon(t);
+ updateResizeTaskBarIcon(t);
+ mMoveTaskButton.setVisibility(View.VISIBLE);
+ mMoveTaskButton.setOnClickListener(this);
+
+ // In accessibility, a single click on the focused app info button will show it
+ AccessibilityManager am = (AccessibilityManager) getContext().
+ getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (am != null && am.isEnabled()) {
+ mApplicationIcon.setOnClickListener(this);
}
}
+ /** Unbinds the bar view from the task */
+ void unbindFromTask() {
+ mTask = null;
+ mApplicationIcon.setImageDrawable(null);
+ mApplicationIcon.setOnClickListener(null);
+ mMoveTaskButton.setOnClickListener(null);
+ }
+
/** Updates the resize task bar button. */
void updateResizeTaskBarIcon(Task t) {
Rect display = mSsp.getWindowRect();
@@ -274,11 +297,6 @@ public class TaskViewHeader extends FrameLayout {
mMoveTaskButton.setImageResource(resId);
}
- /** Unbinds the bar view from the task */
- void unbindFromTask() {
- mApplicationIcon.setImageDrawable(null);
- }
-
/** Animates this task bar dismiss button when launching a task. */
void startLaunchTaskDismissAnimation() {
if (mDismissButton.getVisibility() == View.VISIBLE) {
@@ -441,4 +459,30 @@ public class TaskViewHeader extends FrameLayout {
}
}
}
+
+ @Override
+ public void onClick(View v) {
+ if (v == mApplicationIcon) {
+ // In accessibility, a single click on the focused app info button will show it
+ EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
+ } else if (v == mDismissButton) {
+ TaskView tv = Utilities.findParent(this, TaskView.class);
+ tv.dismissTask();
+
+ // Keep track of deletions by the dismiss button
+ MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
+ Constants.Metrics.DismissSourceHeaderButton);
+ } else if (v == mMoveTaskButton) {
+ EventBus.getDefault().send(new ResizeTaskEvent(mTask));
+ }
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (v == mApplicationIcon) {
+ EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
+ return true;
+ }
+ return false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 6c83beeff1da..690c297da964 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -34,9 +34,9 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
+import com.android.systemui.R;
import com.android.systemui.recents.misc.Utilities;
import com.android.systemui.recents.model.Task;
-import com.android.systemui.R;
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index e7a3c8a3fe72..aaed735990db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1539,6 +1539,7 @@ public abstract class BaseStatusBar extends SystemUI implements
if (viableAction != null) {
Notification stripped = n.clone();
Notification.Builder.stripForDelivery(stripped);
+ stripped.extras.putBoolean("android.rebuild", true);
stripped.actions = new Notification.Action[] { viableAction };
stripped.extras.putBoolean("android.rebuild.contentView", true);
stripped.contentView = null;
@@ -1547,6 +1548,13 @@ public abstract class BaseStatusBar extends SystemUI implements
stripped.extras.putBoolean("android.rebuild.hudView", true);
stripped.headsUpContentView = null;
+ stripped.extras.putParcelable(Notification.EXTRA_LARGE_ICON,
+ stripped.getLargeIcon());
+ if (SystemProperties.getBoolean("debug.strip_third_line", false)) {
+ stripped.extras.putCharSequence(Notification.EXTRA_INFO_TEXT, null);
+ stripped.extras.putCharSequence(Notification.EXTRA_SUMMARY_TEXT, null);
+ }
+
Notification rebuilt = Notification.Builder.rebuild(mContext, stripped);
n.actions = rebuilt.actions;
@@ -1580,27 +1588,35 @@ public abstract class BaseStatusBar extends SystemUI implements
if (remoteInput != null) {
View bigContentView = entry.getExpandedContentView();
if (bigContentView != null) {
- inflateRemoteInput(bigContentView, remoteInput, actions);
+ inflateRemoteInput(bigContentView, entry, remoteInput, actions);
}
View headsUpContentView = entry.getHeadsUpContentView();
if (headsUpContentView != null) {
- inflateRemoteInput(headsUpContentView, remoteInput, actions);
+ inflateRemoteInput(headsUpContentView, entry, remoteInput, actions);
}
}
}
- private void inflateRemoteInput(View view, RemoteInput remoteInput,
+ private void inflateRemoteInput(View view, Entry entry, RemoteInput remoteInput,
Notification.Action[] actions) {
View actionContainerCandidate = view.findViewById(com.android.internal.R.id.actions);
if (actionContainerCandidate instanceof ViewGroup) {
ViewGroup actionContainer = (ViewGroup) actionContainerCandidate;
- actionContainer.removeAllViews();
- actionContainer.addView(
- RemoteInputView.inflate(mContext, actionContainer, actions[0], remoteInput));
+ RemoteInputView riv = inflateRemoteInputView(actionContainer, entry,
+ actions[0], remoteInput);
+ if (riv != null) {
+ actionContainer.removeAllViews();
+ actionContainer.addView(riv);
+ }
}
}
+ protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry,
+ Notification.Action action, RemoteInput remoteInput) {
+ return null;
+ }
+
private final class NotificationClicker implements View.OnClickListener {
public void onClick(final View v) {
if (!(v instanceof ExpandableNotificationRow)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
new file mode 100644
index 000000000000..f243b00143a2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.RemoteInputView;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
+/**
+ * Keeps track of the currently active {@link RemoteInputView}s.
+ */
+public class RemoteInputController {
+
+ private final ArrayList<WeakReference<NotificationData.Entry>> mRemoteInputs = new ArrayList<>();
+ private final StatusBarWindowManager mStatusBarWindowManager;
+ private final HeadsUpManager mHeadsUpManager;
+
+ public RemoteInputController(StatusBarWindowManager sbwm, HeadsUpManager headsUpManager) {
+ mStatusBarWindowManager = sbwm;
+ mHeadsUpManager = headsUpManager;
+ }
+
+ public void addRemoteInput(NotificationData.Entry entry) {
+ Preconditions.checkNotNull(entry);
+
+ boolean found = pruneWeakThenRemoveAndContains(
+ entry /* contains */, null /* remove */);
+ if (!found) {
+ mRemoteInputs.add(new WeakReference<>(entry));
+ }
+
+ apply(entry);
+ }
+
+ public void removeRemoteInput(NotificationData.Entry entry) {
+ Preconditions.checkNotNull(entry);
+
+ pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */);
+
+ apply(entry);
+ }
+
+ private void apply(NotificationData.Entry entry) {
+ mStatusBarWindowManager.setRemoteInputActive(isRemoteInputActive());
+ mHeadsUpManager.setRemoteInputActive(entry, isRemoteInputActive(entry));
+ }
+
+ /**
+ * @return true if {@param entry} has an active RemoteInput
+ */
+ public boolean isRemoteInputActive(NotificationData.Entry entry) {
+ return pruneWeakThenRemoveAndContains(entry /* contains */, null /* remove */);
+ }
+
+ /**
+ * @return true if any entry has an active RemoteInput
+ */
+ public boolean isRemoteInputActive() {
+ pruneWeakThenRemoveAndContains(null /* contains */, null /* remove */);
+ return !mRemoteInputs.isEmpty();
+ }
+
+ /**
+ * Prunes dangling weak references, removes entries referring to {@param remove} and returns
+ * whether {@param contains} is part of the array in a single loop.
+ * @param remove if non-null, removes this entry from the active remote inputs
+ * @return true if {@param contains} is in the set of active remote inputs
+ */
+ private boolean pruneWeakThenRemoveAndContains(
+ NotificationData.Entry contains, NotificationData.Entry remove) {
+ boolean found = false;
+ for (int i = mRemoteInputs.size() - 1; i >= 0; i--) {
+ NotificationData.Entry item = mRemoteInputs.get(i).get();
+ if (item == null || item == remove) {
+ mRemoteInputs.remove(i);
+ } else if (item == contains) {
+ found = true;
+ }
+ }
+ return found;
+ }
+
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index f7c3c670ac4d..cc30882a7089 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -250,6 +250,7 @@ public class SignalClusterView
@Override
public void setNoSims(boolean show) {
mNoSimsVisible = show && !mBlockMobile;
+ apply();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java
new file mode 100644
index 000000000000..497f04494051
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BaseStatusBarHeader.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.RelativeLayout;
+import com.android.systemui.qs.QSPanel;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+
+public abstract class BaseStatusBarHeader extends RelativeLayout implements
+ NetworkControllerImpl.EmergencyListener {
+
+ public BaseStatusBarHeader(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public abstract int getCollapsedHeight();
+ public abstract int getExpandedHeight();
+ public abstract void setExpanded(boolean b);
+ public abstract void setExpansion(float headerExpansionFraction);
+ public abstract void setListening(boolean listening);
+ public abstract void updateEverything();
+ public abstract void setActivityStarter(ActivityStarter activityStarter);
+ public abstract void setQSPanel(QSPanel qSPanel);
+ public abstract void setBatteryController(BatteryController batteryController);
+ public abstract void setNextAlarmController(NextAlarmController nextAlarmController);
+ public abstract void setUserInfoController(UserInfoController userInfoController);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
index 364e884e816e..4ac2c3185b6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarApps.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static android.app.ActivityManager.INVALID_STACK_ID;
+
import android.animation.LayoutTransition;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -742,9 +744,9 @@ class NavigationBarApps extends LinearLayout
private void activateTask(int taskPersistentId) {
// Launch or bring the activity to front.
- IActivityManager manager = ActivityManagerNative.getDefault();
+ final IActivityManager iAm = ActivityManagerNative.getDefault();
try {
- manager.startActivityFromRecents(taskPersistentId, null /* options */);
+ iAm.startActivityFromRecents(taskPersistentId, INVALID_STACK_ID, null /* options */);
} catch (RemoteException e) {
Slog.e(TAG, "Exception when activating a recent task", e);
} catch (IllegalArgumentException e) {
@@ -810,7 +812,7 @@ class NavigationBarApps extends LinearLayout
removeCallbacks(mShowMenuCallback);
break;
}
- return true;
+ return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 011889a49c86..33e514d0a79b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -187,15 +187,6 @@ public class NavigationBarView extends LinearLayout {
mBarTransitions = new NavigationBarTransitions(this);
}
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- ViewRootImpl root = getViewRootImpl();
- if (root != null) {
- root.setDrawDuringWindowsAnimating(true);
- }
- }
-
public BarTransitions getBarTransitions() {
return mBarTransitions;
}
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 08353cbb51c3..3453652404b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -92,7 +92,7 @@ public class NotificationPanelView extends PanelView implements
public static final long DOZE_ANIMATION_DURATION = 700;
private KeyguardAffordanceHelper mAfforanceHelper;
- private StatusBarHeaderView mHeader;
+ private BaseStatusBarHeader mHeader;
private KeyguardUserSwitcher mKeyguardUserSwitcher;
private KeyguardStatusBarView mKeyguardStatusBar;
private QSContainer mQsContainer;
@@ -232,7 +232,7 @@ public class NotificationPanelView extends PanelView implements
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mHeader = (StatusBarHeaderView) findViewById(R.id.header);
+ mHeader = (BaseStatusBarHeader) findViewById(R.id.header);
mHeader.setOnClickListener(this);
mKeyguardStatusBar = (KeyguardStatusBarView) findViewById(R.id.keyguard_header);
mKeyguardStatusView = (KeyguardStatusView) findViewById(R.id.keyguard_status_view);
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 37edc28b765d..05e84889334d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -25,6 +25,7 @@ import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.Notification;
import android.app.PendingIntent;
+import android.app.RemoteInput;
import android.app.StatusBarManager;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
@@ -82,6 +83,7 @@ import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.VelocityTracker;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewStub;
import android.view.WindowManager;
@@ -128,6 +130,7 @@ import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
import com.android.systemui.statusbar.NotificationOverflowContainer;
+import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.SpeedBumpView;
@@ -150,6 +153,7 @@ import com.android.systemui.statusbar.policy.LocationControllerImpl;
import com.android.systemui.statusbar.policy.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.PreviewInflater;
+import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
import com.android.systemui.statusbar.policy.SecurityControllerImpl;
import com.android.systemui.statusbar.policy.UserInfoController;
@@ -299,6 +303,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
StatusBarIconController mIconController;
+ private RemoteInputController mRemoteInputController;
+
// expanded notifications
NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
View mExpandedContents;
@@ -308,7 +314,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private QSPanel mQSPanel;
// top bar
- StatusBarHeaderView mHeader;
+ BaseStatusBarHeader mHeader;
KeyguardStatusBarView mKeyguardStatusBar;
View mKeyguardStatusView;
KeyguardBottomAreaView mKeyguardBottomArea;
@@ -800,7 +806,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
mStatusBarView.setScrimController(mScrimController);
mDozeScrimController = new DozeScrimController(mScrimController, context);
- mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
+ mHeader = (BaseStatusBarHeader) mStatusBarWindow.findViewById(R.id.header);
mHeader.setActivityStarter(this);
mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
@@ -1084,6 +1090,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
return mStatusBarWindow;
}
+ @Override
+ protected RemoteInputView inflateRemoteInputView(ViewGroup root, Entry entry,
+ Notification.Action action, RemoteInput remoteInput) {
+ return RemoteInputView.inflate(mContext, root, entry, action, remoteInput,
+ mRemoteInputController);
+ }
+
public int getStatusBarHeight() {
if (mNaturalBarHeight < 0) {
final Resources res = mContext.getResources();
@@ -2840,6 +2853,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
private void addStatusBarWindow() {
makeStatusBarView();
mStatusBarWindowManager = new StatusBarWindowManager(mContext);
+ mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
+ mHeadsUpManager);
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
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 e9c4e49d7b11..7f5ffafa3b44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -57,9 +57,9 @@ import java.text.NumberFormat;
/**
* The view to manage the header area in the expanded status bar.
*/
-public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener,
+public class StatusBarHeaderView extends BaseStatusBarHeader implements View.OnClickListener,
BatteryController.BatteryStateChangeCallback, NextAlarmController.NextAlarmChangeCallback,
- EmergencyListener {
+ EmergencyListener, TunerService.Tunable {
private boolean mExpanded;
private boolean mListening;
@@ -128,6 +128,8 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
private boolean mShowingDetail;
private boolean mDetailTransitioning;
+ private boolean mAllowExpand = true;
+
public StatusBarHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -232,6 +234,28 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
updateClockCollapsedMargin();
}
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ TunerService.get(mContext).addTunable(this, QSPanel.QS_PAGED_PANEL);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ TunerService.get(mContext).removeTunable(this);
+ }
+
+ @Override
+ public void onTuningChanged(String key, String newValue) {
+ if (QSPanel.QS_PAGED_PANEL.equals(key)) {
+ mAllowExpand = newValue == null || Integer.parseInt(newValue) == 0;
+ if (!mAllowExpand) {
+ setExpanded(false);
+ }
+ }
+ }
+
private void updateClockCollapsedMargin() {
Resources res = getResources();
int padding = res.getDimensionPixelSize(R.dimen.clock_collapsed_bottom_margin);
@@ -290,7 +314,7 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
}
public int getExpandedHeight() {
- return mExpandedHeight;
+ return mAllowExpand ? mExpandedHeight : mCollapsedHeight;
}
public void setListening(boolean listening) {
@@ -302,6 +326,9 @@ public class StatusBarHeaderView extends RelativeLayout implements View.OnClickL
}
public void setExpanded(boolean expanded) {
+ if (!mAllowExpand) {
+ expanded = false;
+ }
boolean changed = expanded != mExpanded;
mExpanded = expanded;
if (changed) {
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 dd62d9b46946..abe51ac3a5d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -123,7 +123,7 @@ public class StatusBarWindowManager {
private void applyFocusableFlag(State state) {
boolean panelFocusable = state.statusBarFocusable && state.panelExpanded;
if (state.keyguardShowing && state.keyguardNeedsInput && state.bouncerShowing
- || BaseStatusBar.ENABLE_REMOTE_INPUT && panelFocusable) {
+ || BaseStatusBar.ENABLE_REMOTE_INPUT && state.remoteInputActive) {
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
@@ -292,6 +292,11 @@ public class StatusBarWindowManager {
apply(mCurrentState);
}
+ public void setRemoteInputActive(boolean remoteInputActive) {
+ mCurrentState.remoteInputActive = remoteInputActive;
+ apply(mCurrentState);
+ }
+
/**
* Set whether the screen brightness is forced to the value we use for doze mode by the status
* bar window.
@@ -326,6 +331,8 @@ public class StatusBarWindowManager {
*/
int statusBarState;
+ boolean remoteInputActive;
+
private boolean isKeyguardShowingAndNotOccluded() {
return keyguardShowing && !keyguardOccluded;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index cfd33589588c..35a17e45c69c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -144,13 +144,6 @@ public class StatusBarWindowView extends FrameLayout {
protected void onAttachedToWindow () {
super.onAttachedToWindow();
- // We really need to be able to animate while window animations are going on
- // so that activities may be started asynchronously from panel animations
- final ViewRootImpl root = getViewRootImpl();
- if (root != null) {
- root.setDrawDuringWindowsAnimating(true);
- }
-
// We need to ensure that our window doesn't suffer from overdraw which would normally
// occur if our window is translucent. Since we are drawing the whole window anyway with
// the scrim, we don't need the window to be cleared in the beginning.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index ed9b123f7431..4f7756e84fcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -102,6 +102,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
private boolean mHeadsUpGoingAway;
private boolean mWaitingOnCollapseWhenGoingAway;
private boolean mIsObserving;
+ private boolean mRemoteInputActive;
public HeadsUpManager(final Context context, View statusBarWindowView) {
mContext = context;
@@ -536,6 +537,18 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
return clicked != null && clicked;
}
+ public void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive) {
+ HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+ if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
+ headsUpEntry.remoteInputActive = remoteInputActive;
+ if (remoteInputActive) {
+ headsUpEntry.removeAutoRemovalCallbacks();
+ } else {
+ headsUpEntry.updateEntry(false /* updatePostTime */);
+ }
+ }
+ }
+
/**
* This represents a notification and how long it is in a heads up mode. It also manages its
* lifecycle automatically when created.
@@ -545,6 +558,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
public long postTime;
public long earliestRemovaltime;
private Runnable mRemoveHeadsUpRunnable;
+ public boolean remoteInputActive;
public void setEntry(final NotificationData.Entry entry) {
this.entry = entry;
@@ -565,12 +579,18 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL
}
public void updateEntry() {
+ updateEntry(true);
+ }
+
+ public void updateEntry(boolean updatePostTime) {
mSortedEntries.remove(HeadsUpEntry.this);
long currentTime = mClock.currentTimeMillis();
earliestRemovaltime = currentTime + mMinimumDisplayTime;
- postTime = Math.max(postTime, currentTime);
+ if (updatePostTime) {
+ postTime = Math.max(postTime, currentTime);
+ }
removeAutoRemovalCallbacks();
- if (!hasFullScreenIntent(entry)) {
+ if (!hasFullScreenIntent(entry) && !mRemoteInputActive) {
long finishTime = postTime + mHeadsUpNotificationDecay;
long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 7d721c22415b..2ad928717320 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.policy;
import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.RemoteInputController;
import android.annotation.NonNull;
import android.app.Notification;
@@ -33,12 +35,15 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
+import java.util.ArrayList;
+
/**
* Host for the remote input.
*/
@@ -51,6 +56,8 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
private PendingIntent mPendingIntent;
private RemoteInput mRemoteInput;
private Notification.Action mAction;
+ private RemoteInputController mController;
+ private NotificationData.Entry mEntry;
public RemoteInputView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -85,12 +92,13 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
});
mEditText.setOnClickListener(this);
mEditText.setInnerFocusable(false);
+ mEditText.mDefocusListener = this;
}
private void sendRemoteInput() {
Bundle results = new Bundle();
results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
- Intent fillInIntent = new Intent();
+ Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
RemoteInput.addResultsToIntent(mAction.getRemoteInputs(), fillInIntent,
results);
@@ -105,7 +113,8 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
}
public static RemoteInputView inflate(Context context, ViewGroup root,
- Notification.Action action, RemoteInput remoteInput) {
+ NotificationData.Entry entry, Notification.Action action, RemoteInput remoteInput,
+ RemoteInputController controller) {
RemoteInputView v = (RemoteInputView)
LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
@@ -113,6 +122,8 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
v.mPendingIntent = action.actionIntent;
v.mRemoteInput = remoteInput;
v.mAction = action;
+ v.mController = controller;
+ v.mEntry = entry;
return v;
}
@@ -122,15 +133,22 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
if (v == mEditText) {
if (!mEditText.isFocusable()) {
mEditText.setInnerFocusable(true);
- InputMethodManager imm = InputMethodManager.getInstance();
- if (imm != null) {
- imm.viewClicked(mEditText);
- imm.showSoftInput(mEditText, 0);
- }
+ mController.addRemoteInput(mEntry);
+ mEditText.mShowImeOnInputConnection = true;
}
}
}
+ public void onDefocus() {
+ mController.removeRemoteInput(mEntry);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mController.removeRemoteInput(mEntry);
+ }
+
/**
* An EditText that changes appearance based on whether it's focusable and becomes
* un-focusable whenever the user navigates away from it or it becomes invisible.
@@ -138,6 +156,8 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
public static class RemoteEditText extends EditText {
private final Drawable mBackground;
+ private RemoteInputView mDefocusListener;
+ boolean mShowImeOnInputConnection;
public RemoteEditText(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -147,6 +167,10 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
private void defocusIfNeeded() {
if (isFocusable() && isEnabled()) {
setInnerFocusable(false);
+ if (mDefocusListener != null) {
+ mDefocusListener.onDefocus();
+ }
+ mShowImeOnInputConnection = false;
}
}
@@ -173,6 +197,28 @@ public class RemoteInputView extends FrameLayout implements View.OnClickListener
return super.onKeyPreIme(keyCode, event);
}
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
+
+ if (mShowImeOnInputConnection && inputConnection != null) {
+ final InputMethodManager imm = InputMethodManager.getInstance();
+ if (imm != null) {
+ // onCreateInputConnection is called by InputMethodManager in the middle of
+ // setting up the connection to the IME; wait with requesting the IME until that
+ // work has completed.
+ post(new Runnable() {
+ @Override
+ public void run() {
+ imm.viewClicked(RemoteEditText.this);
+ imm.showSoftInput(RemoteEditText.this, 0);
+ }
+ });
+ }
+ }
+
+ return inputConnection;
+ }
void setInnerFocusable(boolean focusable) {
setFocusableInTouchMode(focusable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index 40984d477cff..f06e5d354a12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -22,6 +22,7 @@ public interface SecurityController {
String getDeviceOwnerName();
String getProfileOwnerName();
boolean isVpnEnabled();
+ boolean isVpnRestricted();
String getPrimaryVpnName();
String getProfileVpnName();
void onUserSwitched(int newUserId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index e0823b41554d..88f028fa4b7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -162,6 +162,13 @@ public class SecurityControllerImpl implements SecurityController {
}
@Override
+ public boolean isVpnRestricted() {
+ UserHandle currentUser = new UserHandle(mCurrentUserId);
+ return mUserManager.getUserInfo(mCurrentUserId).isRestricted()
+ || mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN, currentUser);
+ }
+
+ @Override
public void removeCallback(SecurityControllerCallback callback) {
synchronized (mCallbacks) {
if (callback == null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
index 703ee6610314..3ac2a943034d 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/QsTuner.java
@@ -350,6 +350,11 @@ public class QsTuner extends Fragment implements Callback {
}
@Override
+ public boolean isVpnRestricted() {
+ return false;
+ }
+
+ @Override
public String getPrimaryVpnName() {
return null;
}
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
index 48e05823f625..f0ca44162dad 100644
--- a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -18,8 +18,11 @@ package com.android.vpndialogs;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.IConnectivityManager;
+import android.os.Bundle;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.text.Html;
@@ -40,43 +43,47 @@ public class ConfirmDialog extends AlertActivity
private IConnectivityManager mService;
- private Button mButton;
-
@Override
- protected void onResume() {
- super.onResume();
- try {
- mPackage = getCallingPackage();
-
- mService = IConnectivityManager.Stub.asInterface(
- ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
-
- if (mService.prepareVpn(mPackage, null, UserHandle.myUserId())) {
- setResult(RESULT_OK);
- finish();
- return;
- }
-
- View view = View.inflate(this, R.layout.confirm, null);
-
- ((TextView) view.findViewById(R.id.warning)).setText(
- Html.fromHtml(
- getString(R.string.warning, VpnConfig.getVpnLabel(this, mPackage)),
- this, null /* tagHandler */));
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mPackage = getCallingPackage();
+ mService = IConnectivityManager.Stub.asInterface(
+ ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
+
+ if (prepareVpn()) {
+ setResult(RESULT_OK);
+ finish();
+ return;
+ }
+ View view = View.inflate(this, R.layout.confirm, null);
+ ((TextView) view.findViewById(R.id.warning)).setText(
+ Html.fromHtml(getString(R.string.warning, getVpnLabel()),
+ this, null /* tagHandler */));
+ mAlertParams.mTitle = getText(R.string.prompt);
+ mAlertParams.mPositiveButtonText = getText(android.R.string.ok);
+ mAlertParams.mPositiveButtonListener = this;
+ mAlertParams.mNegativeButtonText = getText(android.R.string.cancel);
+ mAlertParams.mView = view;
+ setupAlert();
+
+ getWindow().setCloseOnTouchOutside(false);
+ Button button = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+ button.setFilterTouchesWhenObscured(true);
+ }
- mAlertParams.mTitle = getText(R.string.prompt);
- mAlertParams.mPositiveButtonText = getText(android.R.string.ok);
- mAlertParams.mPositiveButtonListener = this;
- mAlertParams.mNegativeButtonText = getText(android.R.string.cancel);
- mAlertParams.mView = view;
- setupAlert();
+ private boolean prepareVpn() {
+ try {
+ return mService.prepareVpn(mPackage, null, UserHandle.myUserId());
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ }
- getWindow().setCloseOnTouchOutside(false);
- mButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
- mButton.setFilterTouchesWhenObscured(true);
- } catch (Exception e) {
- Log.e(TAG, "onResume", e);
- finish();
+ private CharSequence getVpnLabel() {
+ try {
+ return VpnConfig.getVpnLabel(this, mPackage);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalStateException(e);
}
}
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index a2967e8a2c47..7eb80055c4d5 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -1583,9 +1583,21 @@ public class RenderScript {
nContextDeinitToClient(mContext);
mMessageThread.mRun = false;
- try {
- mMessageThread.join();
- } catch(InterruptedException e) {
+
+ // Wait for mMessageThread to join. Try in a loop, in case this thread gets interrupted
+ // during the wait. If interrupted, set the "interrupted" status of the current thread.
+ boolean hasJoined = false, interrupted = false;
+ while (!hasJoined) {
+ try {
+ mMessageThread.join();
+ hasJoined = true;
+ } catch (InterruptedException e) {
+ interrupted = true;
+ }
+ }
+ if (interrupted) {
+ Log.v(LOG_TAG, "Interrupted during wait for MessageThread to join");
+ Thread.currentThread().interrupt();
}
nContextDestroy();
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index 32833785a186..892e9da46a3d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -32,6 +32,7 @@ import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
/**
* Implements "Automatically click on mouse stop" feature.
@@ -56,8 +57,6 @@ import android.view.accessibility.AccessibilityEvent;
*/
public class AutoclickController implements EventStreamTransformation {
- public static final int DEFAULT_CLICK_DELAY_MS = 600;
-
private static final String LOG_TAG = AutoclickController.class.getSimpleName();
private EventStreamTransformation mNext;
@@ -78,7 +77,8 @@ public class AutoclickController implements EventStreamTransformation {
if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (mClickScheduler == null) {
Handler handler = new Handler(mContext.getMainLooper());
- mClickScheduler = new ClickScheduler(handler, DEFAULT_CLICK_DELAY_MS);
+ mClickScheduler =
+ new ClickScheduler(handler, AccessibilityManager.AUTOCLICK_DELAY_DEFAULT);
mClickDelayObserver = new ClickDelayObserver(mUserId, handler);
mClickDelayObserver.start(mContext.getContentResolver(), mClickScheduler);
}
@@ -230,7 +230,7 @@ public class AutoclickController implements EventStreamTransformation {
if (mAutoclickDelaySettingUri.equals(uri)) {
int delay = Settings.Secure.getIntForUser(
mContentResolver, Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
- DEFAULT_CLICK_DELAY_MS, mUserId);
+ AccessibilityManager.AUTOCLICK_DELAY_DEFAULT, mUserId);
mClickScheduler.updateDelay(delay);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
index c3f5c43daeb7..f6b32f7a32ec 100644
--- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java
@@ -112,12 +112,6 @@ class TouchExplorer implements EventStreamTransformation {
// Timeout before trying to decide what the user is trying to do.
private final int mDetermineUserIntentTimeout;
- // Timeout within which we try to detect a tap.
- private final int mTapTimeout;
-
- // Slop between the down and up tap to be a tap.
- private final int mTouchSlop;
-
// Slop between the first and second tap to be a double tap.
private final int mDoubleTapSlop;
@@ -142,9 +136,6 @@ class TouchExplorer implements EventStreamTransformation {
// Command for delayed sending of touch interaction end events.
private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed;
- // Command for delayed sending of a long press.
- private final PerformLongPressDelayed mPerformLongPressDelayed;
-
// Command for exiting gesture detection mode after a timeout.
private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed;
@@ -173,9 +164,6 @@ class TouchExplorer implements EventStreamTransformation {
// Handle to the accessibility manager service.
private final AccessibilityManagerService mAms;
- // Temporary rectangle to avoid instantiation.
- private final Rect mTempRect = new Rect();
-
// Temporary point to avoid instantiation.
private final Point mTempPoint = new Point();
@@ -226,12 +214,9 @@ class TouchExplorer implements EventStreamTransformation {
mAms = service;
mReceivedPointerTracker = new ReceivedPointerTracker();
mInjectedPointerTracker = new InjectedPointerTracker();
- mTapTimeout = ViewConfiguration.getTapTimeout();
mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
- mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
mHandler = new Handler(context.getMainLooper());
- mPerformLongPressDelayed = new PerformLongPressDelayed();
mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed();
mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
mGestureLibrary.setOrientationStyle(8);
@@ -299,7 +284,6 @@ class TouchExplorer implements EventStreamTransformation {
// Remove all pending callbacks.
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
- mPerformLongPressDelayed.cancel();
mExitGestureDetectionModeDelayed.cancel();
mSendTouchExplorationEndDelayed.cancel();
mSendTouchInteractionEndDelayed.cancel();
@@ -437,7 +421,6 @@ class TouchExplorer implements EventStreamTransformation {
// we resent the delayed callback and wait again.
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
- mPerformLongPressDelayed.cancel();
if (mSendTouchExplorationEndDelayed.isPending()) {
mSendTouchExplorationEndDelayed.forceSendAndRemove();
@@ -447,18 +430,7 @@ class TouchExplorer implements EventStreamTransformation {
mSendTouchInteractionEndDelayed.forceSendAndRemove();
}
- // If we have the first tap, schedule a long press and break
- // since we do not want to schedule hover enter because
- // the delayed callback will kick in before the long click.
- // This would lead to a state transition resulting in long
- // pressing the item below the double taped area which is
- // not necessary where accessibility focus is.
- if (mDoubleTapDetector.firstTapDetected()) {
- // We got a tap now post a long press action.
- mPerformLongPressDelayed.post(event, policyFlags);
- break;
- }
- if (!mTouchExplorationInProgress) {
+ if (!mDoubleTapDetector.firstTapDetected() && !mTouchExplorationInProgress) {
if (!mSendHoverEnterAndMoveDelayed.isPending()) {
// Deliver hover enter with a delay to have a chance
// to detect what the user is trying to do.
@@ -478,7 +450,6 @@ class TouchExplorer implements EventStreamTransformation {
// decide what we will actually do next.
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
- mPerformLongPressDelayed.cancel();
} break;
case MotionEvent.ACTION_MOVE: {
final int pointerId = receivedTracker.getPrimaryPointerId();
@@ -521,7 +492,6 @@ class TouchExplorer implements EventStreamTransformation {
mVelocityTracker.clear();
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
- mPerformLongPressDelayed.cancel();
mExitGestureDetectionModeDelayed.post();
// Send accessibility event to announce the start
// of gesture recognition.
@@ -532,28 +502,12 @@ class TouchExplorer implements EventStreamTransformation {
// exploring so start sending events.
mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
mSendHoverExitDelayed.cancel();
- mPerformLongPressDelayed.cancel();
sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE,
pointerIdBits, policyFlags);
}
break;
}
} else {
- // Cancel the long press if pending and the user
- // moved more than the slop.
- if (mPerformLongPressDelayed.isPending()) {
- final float deltaX =
- receivedTracker.getReceivedPointerDownX(pointerId)
- - rawEvent.getX(pointerIndex);
- final float deltaY =
- receivedTracker.getReceivedPointerDownY(pointerId)
- - rawEvent.getY(pointerIndex);
- final double moveDelta = Math.hypot(deltaX, deltaY);
- // The user has moved enough for us to decide.
- if (moveDelta > mTouchSlop) {
- mPerformLongPressDelayed.cancel();
- }
- }
if (mTouchExplorationInProgress) {
sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
@@ -569,9 +523,7 @@ class TouchExplorer implements EventStreamTransformation {
// scheduled sending events.
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
- mPerformLongPressDelayed.cancel();
} else {
- mPerformLongPressDelayed.cancel();
if (mTouchExplorationInProgress) {
// If the user is touch exploring the second pointer may be
// performing a double tap to activate an item without need
@@ -620,9 +572,7 @@ class TouchExplorer implements EventStreamTransformation {
// scheduled sending events.
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
- mPerformLongPressDelayed.cancel();
} else {
- mPerformLongPressDelayed.cancel();
// We are sending events so send exit and gesture
// end since we transition to another state.
sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
@@ -643,7 +593,6 @@ class TouchExplorer implements EventStreamTransformation {
final int pointerId = event.getPointerId(event.getActionIndex());
final int pointerIdBits = (1 << pointerId);
- mPerformLongPressDelayed.cancel();
mVelocityTracker.clear();
if (mSendHoverEnterAndMoveDelayed.isPending()) {
@@ -1110,6 +1059,7 @@ class TouchExplorer implements EventStreamTransformation {
private final GestureDetector mGestureDetector;
private boolean mFirstTapDetected;
private boolean mDoubleTapDetected;
+ private int mPolicyFlags;
DoubleTapDetector(Context context) {
mGestureDetector = new GestureDetector(context, this);
@@ -1117,6 +1067,7 @@ class TouchExplorer implements EventStreamTransformation {
}
public void onMotionEvent(MotionEvent event, int policyFlags) {
+ mPolicyFlags = policyFlags;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mDoubleTapDetected = false;
@@ -1135,6 +1086,11 @@ class TouchExplorer implements EventStreamTransformation {
}
@Override
+ public void onLongPress(MotionEvent e) {
+ maybeSendLongPress(e, mPolicyFlags);
+ }
+
+ @Override
public boolean onSingleTapUp(MotionEvent event) {
mFirstTapDetected = true;
return false;
@@ -1154,6 +1110,38 @@ class TouchExplorer implements EventStreamTransformation {
return true;
}
+ private void maybeSendLongPress(MotionEvent event, int policyFlags) {
+ if (!mDoubleTapDetected) {
+ return;
+ }
+
+ clear();
+
+ // Pointers should not be zero when running this command.
+ if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
+ return;
+ }
+
+ final int pointerIndex = event.getActionIndex();
+ final int pointerId = event.getPointerId(pointerIndex);
+
+ Point clickLocation = mTempPoint;
+ final int result = computeClickLocation(clickLocation);
+
+ if (result == CLICK_LOCATION_NONE) {
+ return;
+ }
+
+ mLongPressingPointerId = pointerId;
+ mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
+ mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
+
+ sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
+
+ mCurrentState = STATE_DELEGATING;
+ sendDownForAllNotInjectedPointers(event, policyFlags);
+ }
+
private void maybeFinishDoubleTap(MotionEvent event, int policyFlags) {
if (!mDoubleTapDetected) {
return;
@@ -1169,7 +1157,6 @@ class TouchExplorer implements EventStreamTransformation {
// Remove pending event deliveries.
mSendHoverEnterAndMoveDelayed.cancel();
mSendHoverExitDelayed.cancel();
- mPerformLongPressDelayed.cancel();
if (mSendTouchExplorationEndDelayed.isPending()) {
mSendTouchExplorationEndDelayed.forceSendAndRemove();
@@ -1178,8 +1165,8 @@ class TouchExplorer implements EventStreamTransformation {
mSendTouchInteractionEndDelayed.forceSendAndRemove();
}
- final int pointerId = event.getPointerId(event.getActionIndex());
- final int pointerIndex = event.findPointerIndex(pointerId);
+ final int pointerIndex = event.getActionIndex();
+ final int pointerId = event.getPointerId(pointerIndex);
Point clickLocation = mTempPoint;
final int result = computeClickLocation(clickLocation);
@@ -1306,65 +1293,6 @@ class TouchExplorer implements EventStreamTransformation {
}
/**
- * Class for delayed sending of long press.
- */
- private final class PerformLongPressDelayed implements Runnable {
- private MotionEvent mEvent;
- private int mPolicyFlags;
-
- public void post(MotionEvent prototype, int policyFlags) {
- mEvent = MotionEvent.obtain(prototype);
- mPolicyFlags = policyFlags;
- mHandler.postDelayed(this, ViewConfiguration.getLongPressTimeout());
- }
-
- public void cancel() {
- if (mEvent != null) {
- mHandler.removeCallbacks(this);
- clear();
- }
- }
-
- private boolean isPending() {
- return mHandler.hasCallbacks(this);
- }
-
- @Override
- public void run() {
- // Pointers should not be zero when running this command.
- if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
- return;
- }
-
- final int pointerId = mEvent.getPointerId(mEvent.getActionIndex());
- final int pointerIndex = mEvent.findPointerIndex(pointerId);
-
- Point clickLocation = mTempPoint;
- final int result = computeClickLocation(clickLocation);
-
- if (result == CLICK_LOCATION_NONE) {
- return;
- }
-
- mLongPressingPointerId = pointerId;
- mLongPressingPointerDeltaX = (int) mEvent.getX(pointerIndex) - clickLocation.x;
- mLongPressingPointerDeltaY = (int) mEvent.getY(pointerIndex) - clickLocation.y;
-
- sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags);
-
- mCurrentState = STATE_DELEGATING;
- sendDownForAllNotInjectedPointers(mEvent, mPolicyFlags);
- clear();
- }
-
- private void clear() {
- mEvent.recycle();
- mEvent = null;
- mPolicyFlags = 0;
- }
- }
-
- /**
* Class for delayed sending of hover enter and move events.
*/
class SendHoverEnterAndMoveDelayed implements Runnable {
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index f2459852d706..2aa039064410 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -55,7 +55,8 @@ public class GestureLauncherService extends SystemService {
* Time in milliseconds in which the power button must be pressed twice so it will be considered
* as a camera launch.
*/
- private static final long CAMERA_POWER_DOUBLE_TAP_TIME_MS = 300;
+ private static final long CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS = 300;
+ private static final long CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS = 120;
/** The listener that receives the gesture event. */
private final GestureEventListener mGestureListener = new GestureEventListener();
@@ -256,14 +257,16 @@ public class GestureLauncherService extends SystemService {
synchronized (this) {
doubleTapInterval = event.getEventTime() - mLastPowerDown;
if (mCameraDoubleTapPowerEnabled
- && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_TIME_MS) {
+ && doubleTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS
+ && doubleTapInterval > CAMERA_POWER_DOUBLE_TAP_MIN_TIME_MS) {
launched = true;
intercept = interactive;
}
mLastPowerDown = event.getEventTime();
}
if (launched) {
- Slog.i(TAG, "Power button double tap gesture detected, launching camera.");
+ Slog.i(TAG, "Power button double tap gesture detected, launching camera. Interval="
+ + doubleTapInterval + "ms");
launched = handleCameraLaunchGesture(false /* useWakelock */,
StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
if (launched) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 9dad7a181a06..ab1d775e3c32 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -37,6 +37,7 @@ import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.app.AppGlobals;
@@ -280,8 +281,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
boolean mSystemReady;
/**
- * Id of the currently selected input method.
+ * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method.
+ * method. This is to be synchronized with the secure settings keyed with
+ * {@link Settings.Secure#DEFAULT_INPUT_METHOD}.
+ *
+ * <p>This can be transiently {@code null} when the system is re-initializing input method
+ * settings, e.g., the system locale is just changed.</p>
+ *
+ * <p>Note that {@link #mCurId} is used to track which IME is being connected to
+ * {@link InputMethodManagerService}.</p>
+ *
+ * @see #mCurId
*/
+ @Nullable
String mCurMethodId;
/**
@@ -311,9 +323,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
EditorInfo mCurAttribute;
/**
- * The input method ID of the input method service that we are currently
+ * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently
* connected to or in the process of connecting to.
+ *
+ * <p>This can be {@code null} when no input method is connected.</p>
+ *
+ * @see #mCurMethodId
*/
+ @Nullable
String mCurId;
/**
@@ -918,7 +935,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
|| (newLocale != null && !newLocale.equals(mLastSystemLocale))) {
if (!updateOnlyWhenLocaleChanged) {
hideCurrentInputLocked(0, null);
- mCurMethodId = null;
unbindCurrentMethodLocked(true, false);
}
if (DEBUG) {
@@ -1474,7 +1490,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
channel.dispose();
}
- void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) {
+ void unbindCurrentMethodLocked(boolean resetCurrentMethodAndClient, boolean savePosition) {
+ if (resetCurrentMethodAndClient) {
+ mCurMethodId = null;
+ }
+
if (mVisibleBound) {
mContext.unbindService(mVisibleConnection);
mVisibleBound = false;
@@ -1501,9 +1521,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mCurId = null;
clearCurMethodLocked();
- if (reportToClient && mCurClient != null) {
- executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
- MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
+ if (resetCurrentMethodAndClient) {
+ unbindCurrentClientLocked();
}
}
@@ -1857,13 +1876,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id));
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Unknown input method from prefs: " + id, e);
- mCurMethodId = null;
unbindCurrentMethodLocked(true, false);
}
mShortcutInputMethodsAndSubtypes.clear();
} else {
// There is no longer an input method set, so stop any current one.
- mCurMethodId = null;
unbindCurrentMethodLocked(true, false);
}
// Here is not the perfect place to reset the switching controller. Ideally
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index c3d32c2a870b..4d325991c8d9 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -29,6 +29,7 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.Manifest;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
import android.app.IActivityManager;
@@ -90,7 +91,6 @@ import libcore.io.IoUtils;
import libcore.util.EmptyArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.os.SomeArgs;
import com.android.internal.os.Zygote;
@@ -290,7 +290,7 @@ class MountService extends IMountService.Stub
private ArrayMap<String, DiskInfo> mDisks = new ArrayMap<>();
/** Map from volume ID to disk */
@GuardedBy("mLock")
- private ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
+ private final ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>();
/** Map from UUID to record */
@GuardedBy("mLock")
@@ -462,11 +462,7 @@ class MountService extends IMountService.Stub
public ObbState(String rawPath, String canonicalPath, int callingUid,
IObbActionListener token, int nonce) {
this.rawPath = rawPath;
- this.canonicalPath = canonicalPath.toString();
-
- final int userId = UserHandle.getUserId(callingUid);
- this.ownerPath = buildObbPath(canonicalPath, userId, false);
- this.voldPath = buildObbPath(canonicalPath, userId, true);
+ this.canonicalPath = canonicalPath;
this.ownerGid = UserHandle.getSharedAppGid(callingUid);
this.token = token;
@@ -475,8 +471,6 @@ class MountService extends IMountService.Stub
final String rawPath;
final String canonicalPath;
- final String ownerPath;
- final String voldPath;
final int ownerGid;
@@ -509,8 +503,6 @@ class MountService extends IMountService.Stub
StringBuilder sb = new StringBuilder("ObbState{");
sb.append("rawPath=").append(rawPath);
sb.append(",canonicalPath=").append(canonicalPath);
- sb.append(",ownerPath=").append(ownerPath);
- sb.append(",voldPath=").append(voldPath);
sb.append(",ownerGid=").append(ownerGid);
sb.append(",token=").append(token);
sb.append(",binder=").append(getBinder());
@@ -569,6 +561,7 @@ class MountService extends IMountService.Stub
private static final int H_VOLUME_MOUNT = 5;
private static final int H_VOLUME_BROADCAST = 6;
private static final int H_INTERNAL_BROADCAST = 7;
+ private static final int H_VOLUME_UNMOUNT = 8;
class MountServiceHandler extends Handler {
public MountServiceHandler(Looper looper) {
@@ -649,6 +642,11 @@ class MountService extends IMountService.Stub
}
break;
}
+ case H_VOLUME_UNMOUNT: {
+ final VolumeInfo vol = (VolumeInfo) msg.obj;
+ unmount(vol.getId());
+ break;
+ }
case H_VOLUME_BROADCAST: {
final StorageVolume userVol = (StorageVolume) msg.obj;
final String envState = userVol.getState();
@@ -683,6 +681,7 @@ class MountService extends IMountService.Stub
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ Preconditions.checkArgument(userId >= 0);
try {
if (Intent.ACTION_USER_ADDED.equals(action)) {
@@ -690,6 +689,16 @@ class MountService extends IMountService.Stub
final int userSerialNumber = um.getUserSerialNumber(userId);
mConnector.execute("volume", "user_added", userId, userSerialNumber);
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+ synchronized (mVolumes) {
+ final int size = mVolumes.size();
+ for (int i = 0; i < size; i++) {
+ final VolumeInfo vol = mVolumes.valueAt(i);
+ if (vol.mountUserId == userId) {
+ vol.mountUserId = UserHandle.USER_NULL;
+ mHandler.obtainMessage(H_VOLUME_UNMOUNT, vol).sendToTarget();
+ }
+ }
+ }
mConnector.execute("volume", "user_removed", userId);
}
} catch (NativeDaemonConnectorException e) {
@@ -757,17 +766,26 @@ class MountService extends IMountService.Stub
* paths never changing, so we outright kill them to pick up new state.
*/
@Deprecated
- private void killMediaProvider() {
+ private void killMediaProvider(List<UserInfo> users) {
+ if (users == null) return;
+
final long token = Binder.clearCallingIdentity();
try {
- final ProviderInfo provider = mPms.resolveContentProvider(MediaStore.AUTHORITY, 0,
- UserHandle.USER_OWNER);
- if (provider != null) {
- final IActivityManager am = ActivityManagerNative.getDefault();
- try {
- am.killApplicationWithAppId(provider.applicationInfo.packageName,
- UserHandle.getAppId(provider.applicationInfo.uid), "vold reset");
- } catch (RemoteException e) {
+ for (UserInfo user : users) {
+ // System user does not have media provider, so skip.
+ if (user.isSystemOnly()) continue;
+
+ final ProviderInfo provider =
+ mPms.resolveContentProvider(MediaStore.AUTHORITY, 0, user.id);
+ if (provider != null) {
+ final IActivityManager am = ActivityManagerNative.getDefault();
+ try {
+ am.killApplicationWithAppId(provider.applicationInfo.packageName,
+ UserHandle.getAppId(provider.applicationInfo.uid), "vold reset");
+ // We only need to run this once. It will kill all users' media processes.
+ break;
+ } catch (RemoteException e) {
+ }
}
}
} finally {
@@ -788,7 +806,9 @@ class MountService extends IMountService.Stub
Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
+ ", mDaemonConnected=" + mDaemonConnected);
if (mSystemReady && mDaemonConnected) {
- killMediaProvider();
+ final UserManager um = UserManager.get(mContext);
+ final List<UserInfo> users = um.getUsers();
+ killMediaProvider(users);
mDisks.clear();
mVolumes.clear();
@@ -799,8 +819,6 @@ class MountService extends IMountService.Stub
mConnector.execute("volume", "reset");
// Tell vold about all existing and started users
- final UserManager um = mContext.getSystemService(UserManager.class);
- final List<UserInfo> users = um.getUsers();
for (UserInfo user : users) {
mConnector.execute("volume", "user_added", user.id, user.serialNumber);
}
@@ -1192,7 +1210,7 @@ class MountService extends IMountService.Stub
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE;
}
- vol.mountUserId = UserHandle.USER_OWNER;
+ vol.mountUserId = ActivityManager.getCurrentUser();
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
} else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
@@ -2285,7 +2303,7 @@ class MountService extends IMountService.Stub
final NativeDaemonEvent event;
try {
- event = mConnector.execute("obb", "path", state.voldPath);
+ event = mConnector.execute("obb", "path", state.canonicalPath);
event.checkCode(VoldResponseCode.AsecPathResult);
return event.getMessage();
} catch (NativeDaemonConnectorException e) {
@@ -3038,14 +3056,14 @@ class MountService extends IMountService.Stub
protected ObbInfo getObbInfo() throws IOException {
ObbInfo obbInfo;
try {
- obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
+ obbInfo = mContainerService.getObbInfo(mObbState.canonicalPath);
} catch (RemoteException e) {
Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
- + mObbState.ownerPath);
+ + mObbState.canonicalPath);
obbInfo = null;
}
if (obbInfo == null) {
- throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
+ throw new IOException("Couldn't read OBB file: " + mObbState.canonicalPath);
}
return obbInfo;
}
@@ -3122,7 +3140,7 @@ class MountService extends IMountService.Stub
int rc = StorageResultCode.OperationSucceeded;
try {
- mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey),
+ mConnector.execute("obb", "mount", mObbState.canonicalPath, new SensitiveArg(hashedKey),
mObbState.ownerGid);
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
@@ -3133,7 +3151,7 @@ class MountService extends IMountService.Stub
if (rc == StorageResultCode.OperationSucceeded) {
if (DEBUG_OBB)
- Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
+ Slog.d(TAG, "Successfully mounted OBB " + mObbState.canonicalPath);
synchronized (mObbMounts) {
addObbStateLocked(mObbState);
@@ -3194,7 +3212,7 @@ class MountService extends IMountService.Stub
int rc = StorageResultCode.OperationSucceeded;
try {
- final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
+ final Command cmd = new Command("obb", "unmount", mObbState.canonicalPath);
if (mForceUnmount) {
cmd.appendArg("force");
}
@@ -3240,49 +3258,6 @@ class MountService extends IMountService.Stub
}
}
- @VisibleForTesting
- public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
- // TODO: allow caller to provide Environment for full testing
- // TODO: extend to support OBB mounts on secondary external storage
-
- // Only adjust paths when storage is emulated
- if (!Environment.isExternalStorageEmulated()) {
- return canonicalPath;
- }
-
- String path = canonicalPath.toString();
-
- // First trim off any external storage prefix
- final UserEnvironment userEnv = new UserEnvironment(userId);
-
- // /storage/emulated/0
- final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath();
- // /storage/emulated_legacy
- final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
- .getAbsolutePath();
-
- if (path.startsWith(externalPath)) {
- path = path.substring(externalPath.length() + 1);
- } else if (path.startsWith(legacyExternalPath)) {
- path = path.substring(legacyExternalPath.length() + 1);
- } else {
- return canonicalPath;
- }
-
- // Handle special OBB paths on emulated storage
- final String obbPath = "Android/obb";
- if (path.startsWith(obbPath)) {
- path = path.substring(obbPath.length() + 1);
-
- final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
- return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
- .getAbsolutePath();
- }
-
- // Handle normal external storage paths
- return new File(userEnv.getExternalStorageDirectory(), path).getAbsolutePath();
- }
-
private static class Callbacks extends Handler {
private static final int MSG_STORAGE_STATE_CHANGED = 1;
private static final int MSG_VOLUME_STATE_CHANGED = 2;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bd10c63edbda..759a4f380ba4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -20,8 +20,11 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.HOME_STACK_ID;
import static android.app.ActivityManager.INVALID_STACK_ID;
+import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static com.android.internal.util.XmlUtils.readBooleanAttribute;
import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -33,6 +36,7 @@ import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
@@ -415,6 +419,9 @@ public final class ActivityManagerService extends ActivityManagerNative
// Lower delay than APP_BOOST_MESSAGE_DELAY to disable the boost
static final int APP_BOOST_TIMEOUT = 2500;
+ // Used to indicate that a task is removed it should also be removed from recents.
+ private static final boolean REMOVE_FROM_RECENTS = true;
+
private static native int nativeMigrateToBoost();
private static native int nativeMigrateFromBoost();
private boolean mIsBoosted = false;
@@ -2732,7 +2739,7 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (ActivityManagerService.this) {
ActivityStack stack = mStackSupervisor.getStack(stackId);
if (stack != null) {
- ActivityRecord r = stack.topRunningActivityLocked(null);
+ ActivityRecord r = stack.topRunningActivityLocked();
if (r != null) {
setFocusedActivityLocked(r, "setFocusedStack");
mStackSupervisor.resumeTopActivitiesLocked(stack, null, null);
@@ -2749,7 +2756,7 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (ActivityManagerService.this) {
TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
if (task != null) {
- ActivityRecord r = task.topRunningActivityLocked(null);
+ ActivityRecord r = task.topRunningActivityLocked();
if (r != null) {
setFocusedActivityLocked(r, "setFocusedTask");
mStackSupervisor.resumeTopActivitiesLocked(task.stack, null, null);
@@ -4185,27 +4192,39 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
- public final int startActivityFromRecents(int taskId, Bundle options) {
+ public final int startActivityFromRecents(int taskId, int launchStackId, Bundle options) {
if (checkCallingPermission(START_TASKS_FROM_RECENTS) != PackageManager.PERMISSION_GRANTED) {
String msg = "Permission Denial: startActivityFromRecents called without " +
START_TASKS_FROM_RECENTS;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
- return startActivityFromRecentsInner(taskId, options);
+ return startActivityFromRecentsInner(taskId, launchStackId, options);
}
- final int startActivityFromRecentsInner(int taskId, Bundle options) {
+ final int startActivityFromRecentsInner(int taskId, int launchStackId, Bundle options) {
final TaskRecord task;
final int callingUid;
final String callingPackage;
final Intent intent;
final int userId;
synchronized (this) {
- task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (launchStackId == HOME_STACK_ID) {
+ throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
+ + taskId + " can't be launch in the home stack.");
+ }
+
+ task = mStackSupervisor.anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
if (task == null) {
- throw new IllegalArgumentException("Task " + taskId + " not found.");
+ throw new IllegalArgumentException(
+ "startActivityFromRecentsInner: Task " + taskId + " not found.");
}
+
+ if (launchStackId != INVALID_STACK_ID && task.stack.mStackId != launchStackId) {
+ mStackSupervisor.moveTaskToStackUncheckedLocked(
+ task, launchStackId, ON_TOP, FORCE_FOCUS, "startActivityFromRecents");
+ }
+
if (task.getRootActivity() != null) {
moveTaskToFrontLocked(task.taskId, 0, options);
return ActivityManager.START_TASK_TO_FRONT;
@@ -4366,12 +4385,16 @@ public final class ActivityManagerService extends ActivityManagerNative
final long origId = Binder.clearCallingIdentity();
try {
boolean res;
+ final boolean finishWithRootActivity =
+ finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
- || (finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY && r == rootR)) {
+ || (finishWithRootActivity && r == rootR)) {
// If requested, remove the task that is associated to this activity only if it
// was the root activity in the task. The result code and data is ignored
- // because we don't support returning them across task boundaries.
- res = removeTaskByIdLocked(tr.taskId, false);
+ // because we don't support returning them across task boundaries. Also, to
+ // keep backwards compatibility we remove the task from recents when finishing
+ // task with root activity.
+ res = removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
if (!res) {
Slog.i(TAG, "Removing task failed to finish activity");
}
@@ -5190,7 +5213,7 @@ public final class ActivityManagerService extends ActivityManagerNative
tr.getBaseIntent().getComponent().getPackageName();
if (tr.userId != userId) continue;
if (!taskPackageName.equals(packageName)) continue;
- removeTaskByIdLocked(tr.taskId, false);
+ removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
}
}
@@ -8547,7 +8570,8 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (this) {
enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
"getTaskThumbnail()");
- TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id, false);
+ final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
+ id, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
if (tr != null) {
return tr.getTaskThumbnailLocked();
}
@@ -8660,7 +8684,8 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void setTaskResizeable(int taskId, boolean resizeable) {
synchronized (this) {
- TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId, false);
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+ taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
if (task == null) {
Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
return;
@@ -8685,7 +8710,32 @@ public final class ActivityManagerService extends ActivityManagerNative
Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
return;
}
- mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode);
+ // Place the task in the right stack if it isn't there already based on
+ // the requested bounds.
+ // The stack transition logic is:
+ // - a null bounds on a freeform task moves that task to fullscreen
+ // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
+ // that task to freeform
+ // - otherwise the task is not moved
+ // Note it's not allowed to resize a home stack task, or a docked task.
+ int stackId = task.stack.mStackId;
+ if (stackId == HOME_STACK_ID || stackId == DOCKED_STACK_ID) {
+ throw new IllegalArgumentException("trying to resizeTask on a "
+ + "home or docked task");
+ }
+ if (bounds == null && stackId == FREEFORM_WORKSPACE_STACK_ID) {
+ stackId = FULLSCREEN_WORKSPACE_STACK_ID;
+ } else if (bounds != null && stackId != FREEFORM_WORKSPACE_STACK_ID ) {
+ stackId = FREEFORM_WORKSPACE_STACK_ID;
+ }
+ boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
+ if (stackId != task.stack.mStackId) {
+ mStackSupervisor.moveTaskToStackUncheckedLocked(
+ task, stackId, ON_TOP, !FORCE_FOCUS, "resizeTask");
+ preserveWindow = false;
+ }
+
+ mStackSupervisor.resizeTaskLocked(task, bounds, resizeMode, preserveWindow);
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -8736,9 +8786,12 @@ public final class ActivityManagerService extends ActivityManagerNative
mWindowManager.executeAppTransition();
}
- private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess) {
- mRecentTasks.remove(tr);
- tr.removedFromRecents();
+ private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess,
+ boolean removeFromRecents) {
+ if (removeFromRecents) {
+ mRecentTasks.remove(tr);
+ tr.removedFromRecents();
+ }
ComponentName component = tr.getBaseIntent().getComponent();
if (component == null) {
Slog.w(TAG, "No component for base intent of task: " + tr);
@@ -8815,7 +8868,7 @@ public final class ActivityManagerService extends ActivityManagerNative
ComponentName cn = tr.intent.getComponent();
if (cn != null && cn.getPackageName().equals(packageName)) {
// If the package name matches, remove the task.
- removeTaskByIdLocked(tr.taskId, true);
+ removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
}
}
}
@@ -8833,7 +8886,7 @@ public final class ActivityManagerService extends ActivityManagerNative
final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
&& (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
if (sameComponent) {
- removeTaskByIdLocked(tr.taskId, false);
+ removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
}
}
}
@@ -8843,13 +8896,16 @@ public final class ActivityManagerService extends ActivityManagerNative
*
* @param taskId Identifier of the task to be removed.
* @param killProcess Kill any process associated with the task if possible.
+ * @param removeFromRecents Whether to also remove the task from recents.
* @return Returns true if the given task was found and removed.
*/
- private boolean removeTaskByIdLocked(int taskId, boolean killProcess) {
- TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId, false);
+ private boolean removeTaskByIdLocked(int taskId, boolean killProcess,
+ boolean removeFromRecents) {
+ final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
+ taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
if (tr != null) {
tr.removeTaskActivitiesLocked();
- cleanUpRemovedTaskLocked(tr, killProcess);
+ cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
if (tr.isPersistable) {
notifyTaskPersisterLocked(null, true);
}
@@ -8866,7 +8922,7 @@ public final class ActivityManagerService extends ActivityManagerNative
"removeTask()");
long ident = Binder.clearCallingIdentity();
try {
- return removeTaskByIdLocked(taskId, true);
+ return removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9175,7 +9231,8 @@ public final class ActivityManagerService extends ActivityManagerNative
long ident = Binder.clearCallingIdentity();
try {
synchronized (this) {
- TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(taskId, false);
+ final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
+ taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
return tr != null && tr.stack != null && tr.stack.isHomeStack();
}
} finally {
@@ -11308,7 +11365,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public boolean isTopActivityImmersive() {
enforceNotIsolatedCaller("startActivity");
synchronized (this) {
- ActivityRecord r = getFocusedStack().topRunningActivityLocked(null);
+ ActivityRecord r = getFocusedStack().topRunningActivityLocked();
return (r != null) ? r.immersive : false;
}
}
@@ -11893,7 +11950,8 @@ public final class ActivityManagerService extends ActivityManagerNative
updateCurrentProfileIdsLocked();
mRecentTasks.clear();
- mRecentTasks.addAll(mTaskPersister.restoreTasksLocked());
+ mRecentTasks.addAll(mTaskPersister.restoreTasksLocked(
+ getUserManagerLocked().getUserIds()));
mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
mTaskPersister.startPersisting();
@@ -17526,6 +17584,25 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
+ public void removeStack(int stackId) {
+ enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
+ "detahStack()");
+ if (stackId == HOME_STACK_ID) {
+ throw new IllegalArgumentException("Removing home stack is not allowed.");
+ }
+ synchronized (this) {
+ ActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack != null) {
+ ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
+ !REMOVE_FROM_RECENTS);
+ }
+ }
+ }
+ }
+
+ @Override
public void updatePersistentConfiguration(Configuration values) {
enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
"updateConfiguration()");
@@ -17698,7 +17775,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// If the configuration changed, and the caller is not already
// in the process of starting an activity, then find the top
// activity to check if its configuration needs to change.
- starting = mainStack.topRunningActivityLocked(null);
+ starting = mainStack.topRunningActivityLocked();
}
if (starting != null) {
@@ -20644,9 +20721,6 @@ public final class ActivityManagerService extends ActivityManagerNative
// Kill all the processes for the user.
forceStopUserLocked(userId, "finish user");
}
-
- // Explicitly remove the old information in mRecentTasks.
- mRecentTasks.removeTasksForUserLocked(userId);
}
for (int i=0; i<callbacks.size(); i++) {
@@ -20665,6 +20739,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ void onUserRemovedLocked(int userId) {
+ mRecentTasks.removeTasksForUserLocked(userId);
+ }
+
@Override
public UserInfo getCurrentUser() {
if ((checkCallingPermission(INTERACT_ACROSS_USERS)
@@ -20949,6 +21027,13 @@ public final class ActivityManagerService extends ActivityManagerNative
return homeActivity == null ? null : homeActivity.realActivity;
}
}
+
+ @Override
+ public void onUserRemoved(int userId) {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.onUserRemovedLocked(userId);
+ }
+ }
}
private final class SleepTokenImpl extends SleepToken {
@@ -21003,7 +21088,8 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (ActivityManagerService.this) {
long origId = Binder.clearCallingIdentity();
try {
- if (!removeTaskByIdLocked(mTaskId, false)) {
+ // We remove the task from recents to preserve backwards
+ if (!removeTaskByIdLocked(mTaskId, false, REMOVE_FROM_RECENTS)) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
} finally {
@@ -21034,7 +21120,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public void moveToFront() {
checkCaller();
// Will bring task to front if it already has a root activity.
- startActivityFromRecentsInner(mTaskId, null);
+ startActivityFromRecentsInner(mTaskId, INVALID_STACK_ID, null);
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 24b90d81996d..4671cb0a94e3 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -477,8 +477,8 @@ final class ActivityRecord {
}
}
- boolean isNotResolverActivity() {
- return !ResolverActivity.class.getName().equals(realActivity.getClassName());
+ boolean isResolverActivity() {
+ return ResolverActivity.class.getName().equals(realActivity.getClassName());
}
ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
@@ -605,7 +605,7 @@ final class ActivityRecord {
_intent.getData() == null &&
_intent.getType() == null &&
(intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
- isNotResolverActivity()) {
+ !isResolverActivity()) {
// This sure looks like a home activity!
mActivityType = HOME_ACTIVITY_TYPE;
} else if (realActivity.getClassName().contains(RECENTS_PACKAGE_NAME)) {
@@ -774,7 +774,7 @@ final class ActivityRecord {
boolean unsent = true;
if ((state == ActivityState.RESUMED
|| (service.isSleeping() && task.stack != null
- && task.stack.topRunningActivityLocked(null) == this))
+ && task.stack.topRunningActivityLocked() == this))
&& app != null && app.thread != null) {
try {
ArrayList<ReferrerIntent> ar = new ArrayList<>(1);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a796ea7d4ab5..cdb00ef17823 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -403,9 +403,9 @@ final class ActivityStack {
return mStackSupervisor.okToShowLocked(r);
}
- final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+ final ActivityRecord topRunningActivityLocked() {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked(notTop);
+ ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked();
if (r != null) {
return r;
}
@@ -689,7 +689,7 @@ final class ActivityStack {
// NOTE: If {@link TaskRecord#topRunningActivityLocked} return is not null then it is
// okay to show the activity when locked.
if (mStackSupervisor.isCurrentProfileLocked(task.userId)
- || task.topRunningActivityLocked(null) != null) {
+ || task.topRunningActivityLocked() != null) {
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() +
" moving " + task + " to top");
mTaskHistory.remove(i);
@@ -1105,7 +1105,7 @@ final class ActivityStack {
mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
} else {
mStackSupervisor.checkReadyForSleepLocked();
- ActivityRecord top = topStack.topRunningActivityLocked(null);
+ ActivityRecord top = topStack.topRunningActivityLocked();
if (top == null || (prev != null && top != prev)) {
// If there are no more activities available to run,
// do resume anyway to start something. Also if the top
@@ -1153,7 +1153,7 @@ final class ActivityStack {
next.results = null;
next.newIntents = null;
- if (next.isHomeActivity() && next.isNotResolverActivity()) {
+ if (next.isHomeActivity()) {
ProcessRecord app = next.task.mActivities.get(0).app;
if (app != null && app != mService.mHomeProcess) {
mService.mHomeProcess = app;
@@ -1326,7 +1326,7 @@ final class ActivityStack {
if (focusedStackId != HOME_STACK_ID) {
return true;
}
- ActivityRecord topHomeActivity = focusedStack.topRunningActivityLocked(null);
+ ActivityRecord topHomeActivity = focusedStack.topRunningActivityLocked();
return topHomeActivity == null || !topHomeActivity.isHomeActivity();
}
@@ -1387,7 +1387,7 @@ final class ActivityStack {
*/
final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
boolean preserveWindows) {
- ActivityRecord top = topRunningActivityLocked(null);
+ ActivityRecord top = topRunningActivityLocked();
if (top == null) {
return;
}
@@ -1643,7 +1643,7 @@ final class ActivityStack {
* starting window displayed then remove that starting window. It is possible that the activity
* in this state will never resumed in which case that starting window will be orphaned. */
void cancelInitializingActivities() {
- final ActivityRecord topActivity = topRunningActivityLocked(null);
+ final ActivityRecord topActivity = topRunningActivityLocked();
boolean aboveTop = true;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
@@ -1719,7 +1719,7 @@ final class ActivityStack {
cancelInitializingActivities();
// Find the first activity that is not finishing.
- final ActivityRecord next = topRunningActivityLocked(null);
+ final ActivityRecord next = topRunningActivityLocked();
// Remember how we'll process this pause/resume situation, and ensure
// that the state is reset however we wind up proceeding.
@@ -2037,7 +2037,7 @@ final class ActivityStack {
// We should be all done, but let's just make sure our activity
// is still at the top and schedule another run if something
// weird happened.
- ActivityRecord nextNext = topRunningActivityLocked(null);
+ ActivityRecord nextNext = topRunningActivityLocked();
if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_STATES,
"Activity config changed during resume: " + next
+ ", new next: " + nextNext);
@@ -2170,12 +2170,12 @@ final class ActivityStack {
// Calculate maximum possible position for this task.
int maxPosition = mTaskHistory.size();
if (!mStackSupervisor.isCurrentProfileLocked(task.userId)
- && task.topRunningActivityLocked(null) == null) {
+ && task.topRunningActivityLocked() == null) {
// Put non-current user tasks below current user tasks.
while (maxPosition > 0) {
final TaskRecord tmpTask = mTaskHistory.get(maxPosition - 1);
if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId)
- || tmpTask.topRunningActivityLocked(null) == null) {
+ || tmpTask.topRunningActivityLocked() == null) {
break;
}
maxPosition--;
@@ -2217,13 +2217,13 @@ final class ActivityStack {
int taskNdx = mTaskHistory.size();
final boolean notShownWhenLocked =
(newActivity != null && (newActivity.info.flags & FLAG_SHOW_FOR_ALL_USERS) == 0)
- || (newActivity == null && task.topRunningActivityLocked(null) == null);
+ || (newActivity == null && task.topRunningActivityLocked() == null);
if (!mStackSupervisor.isCurrentProfileLocked(task.userId) && notShownWhenLocked) {
// Put non-current user tasks below current user tasks.
while (--taskNdx >= 0) {
final TaskRecord tmpTask = mTaskHistory.get(taskNdx);
if (!mStackSupervisor.isCurrentProfileLocked(tmpTask.userId)
- || tmpTask.topRunningActivityLocked(null) == null) {
+ || tmpTask.topRunningActivityLocked() == null) {
break;
}
}
@@ -2769,7 +2769,7 @@ final class ActivityStack {
private void adjustFocusedActivityLocked(ActivityRecord r, String reason) {
if (mStackSupervisor.isFrontStack(this) && mService.mFocusedActivity == r) {
- ActivityRecord next = topRunningActivityLocked(null);
+ ActivityRecord next = topRunningActivityLocked();
final String myReason = reason + " adjustFocus";
if (next != r) {
if (next != null && (mStackId == FREEFORM_WORKSPACE_STACK_ID
@@ -2812,7 +2812,7 @@ final class ActivityStack {
if (stack == null) {
return false;
}
- final ActivityRecord top = stack.topRunningActivityLocked(null);
+ final ActivityRecord top = stack.topRunningActivityLocked();
if (top == null) {
return false;
}
@@ -2913,7 +2913,7 @@ final class ActivityStack {
}
final void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
- ActivityRecord r = topRunningActivityLocked(null);
+ ActivityRecord r = topRunningActivityLocked();
if (r != null && r.app == app) {
// If the top running activity is from this crashing
// process, then terminate it to avoid getting in a loop.
@@ -3618,7 +3618,7 @@ final class ActivityStack {
void releaseBackgroundResources(ActivityRecord r) {
if (hasVisibleBehindActivity() &&
!mHandler.hasMessages(RELEASE_BACKGROUND_RESOURCES_TIMEOUT_MSG)) {
- if (r == topRunningActivityLocked(null)) {
+ if (r == topRunningActivityLocked()) {
// Don't release the top activity if it has requested to run behind the next
// activity.
return;
@@ -3767,7 +3767,7 @@ final class ActivityStack {
final void updateTransitLocked(int transit, Bundle options) {
if (options != null) {
- ActivityRecord r = topRunningActivityLocked(null);
+ ActivityRecord r = topRunningActivityLocked();
if (r != null && r.state != ActivityState.RESUMED) {
r.updateOptionsLocked(options);
} else {
@@ -3840,7 +3840,7 @@ final class ActivityStack {
}
// Set focus to the top running activity of this stack.
- ActivityRecord r = topRunningActivityLocked(null);
+ ActivityRecord r = topRunningActivityLocked();
mService.setFocusedActivityLocked(r, reason);
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
@@ -4473,7 +4473,7 @@ final class ActivityStack {
}
ActivityRecord restartPackage(String packageName) {
- ActivityRecord starting = topRunningActivityLocked(null);
+ ActivityRecord starting = topRunningActivityLocked();
// All activities that came from the package must be
// restarted as if there was a config change.
@@ -4545,7 +4545,8 @@ final class ActivityStack {
if (mTaskHistory.isEmpty()) {
if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this);
- if (isOnHomeDisplay()) {
+ // We only need to adjust focused stack if this stack is in focus.
+ if (isOnHomeDisplay() && mStackSupervisor.isFrontStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
if (mFullscreen || !adjustFocusToNextVisibleStackLocked(null, myReason)) {
mStackSupervisor.moveHomeStackToFront(myReason);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 6b5f2056ec82..8bec7f7cad97 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -66,7 +66,6 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
@@ -191,6 +190,9 @@ public final class ActivityStackSupervisor implements DisplayListener {
// Force the focus to change to the stack we are moving a task to..
static final boolean FORCE_FOCUS = true;
+ // Restore task from the saved recents if it can't be found in any live stack.
+ static final boolean RESTORE_FROM_RECENTS = true;
+
// Activity actions an app cannot start if it uses a permission which is not granted.
private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
new ArrayMap<>();
@@ -541,7 +543,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
TaskRecord anyTaskForIdLocked(int id) {
- return anyTaskForIdLocked(id, true);
+ return anyTaskForIdLocked(id, RESTORE_FROM_RECENTS, INVALID_STACK_ID);
}
/**
@@ -549,8 +551,10 @@ public final class ActivityStackSupervisor implements DisplayListener {
* @param id Id of the task we would like returned.
* @param restoreFromRecents If the id was not in the active list, but was found in recents,
* restore the task from recents to the active list.
+ * @param stackId The stack to restore the task to (default launch stack will be used
+ * if stackId is {@link android.app.ActivityManager#INVALID_STACK_ID}).
*/
- TaskRecord anyTaskForIdLocked(int id, boolean restoreFromRecents) {
+ TaskRecord anyTaskForIdLocked(int id, boolean restoreFromRecents, int stackId) {
int numDisplays = mActivityDisplays.size();
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
@@ -575,7 +579,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
return task;
}
- if (!restoreRecentTaskLocked(task, INVALID_STACK_ID)) {
+ if (!restoreRecentTaskLocked(task, stackId)) {
if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
"Couldn't restore task id=" + id + " found in recents");
return null;
@@ -610,7 +614,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (mCurTaskId <= 0) {
mCurTaskId = 1;
}
- } while (anyTaskForIdLocked(mCurTaskId, false) != null);
+ } while (anyTaskForIdLocked(mCurTaskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID) != null);
return mCurTaskId;
}
@@ -623,7 +627,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (resumedActivity == null || resumedActivity.app == null) {
resumedActivity = stack.mPausingActivity;
if (resumedActivity == null || resumedActivity.app == null) {
- resumedActivity = stack.topRunningActivityLocked(null);
+ resumedActivity = stack.topRunningActivityLocked();
}
}
return resumedActivity;
@@ -639,7 +643,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (!isFrontStack(stack)) {
continue;
}
- ActivityRecord hr = stack.topRunningActivityLocked(null);
+ ActivityRecord hr = stack.topRunningActivityLocked();
if (hr != null) {
if (hr.app == null && app.uid == hr.info.applicationInfo.uid
&& processName.equals(hr.processName)) {
@@ -823,7 +827,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
ActivityRecord topRunningActivityLocked() {
final ActivityStack focusedStack = mFocusedStack;
- ActivityRecord r = focusedStack.topRunningActivityLocked(null);
+ ActivityRecord r = focusedStack.topRunningActivityLocked();
if (r != null) {
return r;
}
@@ -833,7 +837,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (stack != focusedStack && isFrontStack(stack)) {
- r = stack.topRunningActivityLocked(null);
+ r = stack.topRunningActivityLocked();
if (r != null) {
return r;
}
@@ -1096,7 +1100,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
} while (!outResult.timeout && outResult.who == null);
} else if (res == ActivityManager.START_TASK_TO_FRONT) {
- ActivityRecord r = stack.topRunningActivityLocked(null);
+ ActivityRecord r = stack.topRunningActivityLocked();
if (r.nowVisible && r.state == RESUMED) {
outResult.timeout = false;
outResult.who = new ComponentName(r.info.packageName, r.info.name);
@@ -1262,7 +1266,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
r.userId, System.identityHashCode(r),
task.taskId, r.shortComponentName);
}
- if (r.isHomeActivity() && r.isNotResolverActivity()) {
+ if (r.isHomeActivity()) {
// Home process is the root process of the task.
mService.mHomeProcess = task.mActivities.get(0).app;
}
@@ -1799,10 +1803,14 @@ public final class ActivityStackSupervisor implements DisplayListener {
return container.mStack;
}
- // The fullscreen stack is the only stack that can contain any task regardless of if
- // the task is resizeable or not. So, we let the task go in the fullscreen task if it
- // is the focus stack.
- if (mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
+ // The fullscreen stack can contain any task regardless of if the task is resizeable
+ // or not. So, we let the task go in the fullscreen task if it is the focus stack.
+ // If the freeform stack has focus, and the activity to be launched is resizeable,
+ // we can also put it in the focused stack.
+ final boolean canUseFocusedStack =
+ mFocusedStack.mStackId == FULLSCREEN_WORKSPACE_STACK_ID
+ || mFocusedStack.mStackId == FREEFORM_WORKSPACE_STACK_ID && r.info.resizeable;
+ if (canUseFocusedStack
&& (!newTask || mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Have a focused stack=" + mFocusedStack);
@@ -1855,17 +1863,11 @@ public final class ActivityStackSupervisor implements DisplayListener {
boolean overrideBounds = false;
Rect newBounds = null;
- if (r.info.resizeable || (inTask != null && inTask.mResizeable)) {
- if (intent.hasExtra(ActivityOptions.KEY_BOUNDS)) {
+ if (options != null && (r.info.resizeable || (inTask != null && inTask.mResizeable))) {
+ ActivityOptions opts = new ActivityOptions(options);
+ if (opts.hasBounds()) {
overrideBounds = true;
- newBounds = Rect.unflattenFromString(
- intent.getStringExtra(ActivityOptions.KEY_BOUNDS));
- } else if (options != null) {
- ActivityOptions opts = new ActivityOptions(options);
- if (opts.hasBounds()) {
- overrideBounds = true;
- newBounds = opts.getBounds();
- }
+ newBounds = opts.getBounds();
}
}
@@ -2024,6 +2026,14 @@ public final class ActivityStackSupervisor implements DisplayListener {
reuseTask = inTask;
} else {
inTask = null;
+ // Launch ResolverActivity in the source task, so that it stays in the task
+ // bounds when in freeform workspace.
+ // Also put noDisplay activities in the source task. These by itself can
+ // be placed in any task/stack, however it could launch other activities
+ // like ResolverActivity, and we want those to stay in the original task.
+ if (r.isResolverActivity() || r.noDisplay) {
+ addingToTask = true;
+ }
}
if (inTask == null) {
@@ -2971,7 +2981,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
- ActivityRecord r = stack.topRunningActivityLocked(null);
+ ActivityRecord r = stack.topRunningActivityLocked();
mTmpBounds.clear();
mTmpConfigs.clear();
@@ -3002,7 +3012,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
// In this case we make all other static stacks fullscreen and move all
// docked stack tasks to the fullscreen stack.
for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
- if (i != DOCKED_STACK_ID) {
+ if (i != DOCKED_STACK_ID && getStack(i) != null) {
resizeStackLocked(i, null, preserveWindows);
}
}
@@ -3058,7 +3068,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
- void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode) {
+ void resizeTaskLocked(TaskRecord task, Rect bounds, int resizeMode, boolean preserveWindow) {
if (!task.mResizeable) {
Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
return;
@@ -3066,7 +3076,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
// If this is a forced resize, let it go through even if the bounds is not changing,
// as we might need a relayout due to surface size change (to/from fullscreen).
- final boolean forced = (resizeMode == RESIZE_MODE_FORCED);
+ final boolean forced = (resizeMode & RESIZE_MODE_FORCED) != 0;
if (task.mBounds != null && task.mBounds.equals(bounds) && !forced) {
// Nothing to do here...
return;
@@ -3084,35 +3094,21 @@ public final class ActivityStackSupervisor implements DisplayListener {
return;
}
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + task.taskId);
+ // Do not move the task to another stack here.
+ // This method assumes that the task is already placed in the right stack.
+ // we do not mess with that decision and we only do the resize!
- // The stack of a task is determined by its size (fullscreen vs non-fullscreen).
- // Place the task in the right stack if it isn't there already based on the requested
- // bounds.
- int stackId = task.stack.mStackId;
- if (bounds == null && stackId != FULLSCREEN_WORKSPACE_STACK_ID) {
- stackId = FULLSCREEN_WORKSPACE_STACK_ID;
- } else if (bounds != null
- && stackId != FREEFORM_WORKSPACE_STACK_ID && stackId != DOCKED_STACK_ID) {
- stackId = FREEFORM_WORKSPACE_STACK_ID;
- }
- final boolean changedStacks = stackId != task.stack.mStackId;
- if (changedStacks) {
- moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, "resizeTask");
- }
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + task.taskId);
final Configuration overrideConfig = task.updateOverrideConfiguration(bounds);
- // This variable holds information whether the configuration didn't change in a signficant
+ // This variable holds information whether the configuration didn't change in a significant
// way and the activity was kept the way it was. If it's false, it means the activity had
// to be relaunched due to configuration change.
boolean kept = true;
if (overrideConfig != null) {
- ActivityRecord r = task.topRunningActivityLocked(null);
+ ActivityRecord r = task.topRunningActivityLocked();
if (r != null) {
final ActivityStack stack = task.stack;
- final boolean preserveWindow = !changedStacks &&
- (resizeMode == RESIZE_MODE_USER
- || resizeMode == RESIZE_MODE_SYSTEM_SCREEN_ROTATION);
kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
// All other activities must be made visible with their correct configuration.
ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
@@ -3153,7 +3149,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
* Restores a recent task to a stack
* @param task The recent task to be restored.
* @param stackId The stack to restore the task to (default launch stack will be used
- * if stackId is invalid).
+ * if stackId is {@link android.app.ActivityManager#INVALID_STACK_ID}).
* @return true if the task has been restored successfully.
*/
private boolean restoreRecentTaskLocked(TaskRecord task, int stackId) {
@@ -3204,7 +3200,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
* @param reason Reason the task is been moved.
* @return The stack the task was moved to.
*/
- private ActivityStack moveTaskToStackUncheckedLocked(
+ ActivityStack moveTaskToStackUncheckedLocked(
TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) {
final ActivityRecord r = task.getTopActivity();
final boolean wasFocused = isFrontStack(task.stack) && (topRunningActivityLocked() == r);
@@ -3252,7 +3248,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
// and then add a new one. This call will tell window manager about this, so it can
// preserve the old window until the new one is drawn. This prevents having a gap
// between the removal and addition, in which no window is visible. We also want the
- // entrace of the new window to be properly animated.
+ // entrance of the new window to be properly animated.
ActivityRecord r = task.getTopActivity();
mWindowManager.setReplacingWindow(r.appToken, true /* animate */);
}
@@ -3261,12 +3257,15 @@ public final class ActivityStackSupervisor implements DisplayListener {
// Make sure the task has the appropriate bounds/size for the stack it is in.
if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
- resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM);
+ resizeTaskLocked(task, stack.mBounds,
+ RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
} else if (stackId == FREEFORM_WORKSPACE_STACK_ID
&& task.mBounds == null && task.mLastNonFullscreenBounds != null) {
- resizeTaskLocked(task, task.mLastNonFullscreenBounds, RESIZE_MODE_SYSTEM);
+ resizeTaskLocked(task, task.mLastNonFullscreenBounds,
+ RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
} else if (stackId == DOCKED_STACK_ID) {
- resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM);
+ resizeTaskLocked(task, stack.mBounds,
+ RESIZE_MODE_SYSTEM, !PRESERVE_WINDOWS);
}
// The task might have already been running and its visibility needs to be synchronized with
@@ -3738,7 +3737,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
- final ActivityRecord r = stack.topRunningActivityLocked(null);
+ final ActivityRecord r = stack.topRunningActivityLocked();
final ActivityState state = r == null ? DESTROYED : r.state;
if (isFrontStack(stack)) {
if (r == null) Slog.e(TAG,
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index c36fd06f633b..26264e5da147 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -196,7 +196,7 @@ public final class CompatModePackages {
}
public boolean getFrontActivityAskCompatModeLocked() {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
+ ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
if (r == null) {
return false;
}
@@ -208,7 +208,7 @@ public final class CompatModePackages {
}
public void setFrontActivityAskCompatModeLocked(boolean ask) {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
+ ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
if (r != null) {
setPackageAskCompatModeLocked(r.packageName, ask);
}
@@ -230,7 +230,7 @@ public final class CompatModePackages {
}
public int getFrontActivityScreenCompatModeLocked() {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
+ ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
if (r == null) {
return ActivityManager.COMPAT_MODE_UNKNOWN;
}
@@ -238,7 +238,7 @@ public final class CompatModePackages {
}
public void setFrontActivityScreenCompatModeLocked(int mode) {
- ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked(null);
+ ActivityRecord r = mService.getFocusedStack().topRunningActivityLocked();
if (r == null) {
Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity");
return;
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index aa154a7c3850..871331b770e2 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -35,6 +35,7 @@ import android.util.SparseArray;
import android.util.Xml;
import android.os.Process;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
@@ -330,7 +331,7 @@ public class TaskPersister {
return null;
}
- ArrayList<TaskRecord> restoreTasksLocked() {
+ ArrayList<TaskRecord> restoreTasksLocked(final int [] validUserIds) {
final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
@@ -362,15 +363,18 @@ public class TaskPersister {
if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" +
task);
if (task != null) {
- task.isPersistable = true;
// XXX Don't add to write queue... there is no reason to write
// out the stuff we just read, if we don't write it we will
// read the same thing again.
//mWriteQueue.add(new TaskWriteQueueItem(task));
- tasks.add(task);
final int taskId = task.taskId;
- recoveredTaskIds.add(taskId);
mStackSupervisor.setNextTaskId(taskId);
+ // Check if it's a valid user id. Don't add tasks for removed users.
+ if (ArrayUtils.contains(validUserIds, task.userId)) {
+ task.isPersistable = true;
+ tasks.add(task);
+ recoveredTaskIds.add(taskId);
+ }
} else {
Slog.e(TAG, "Unable to restore taskFile=" + taskFile + ": " +
fileToString(taskFile));
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 7e14b2b78f0f..77dbad4340ed 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -557,11 +557,11 @@ final class TaskRecord {
return null;
}
- ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+ ActivityRecord topRunningActivityLocked() {
if (stack != null) {
for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = mActivities.get(activityNdx);
- if (!r.finishing && r != notTop && stack.okToShowLocked(r)) {
+ if (!r.finishing && stack.okToShowLocked(r)) {
return r;
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e49a7e49822c..c4b57f1945a1 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -501,7 +501,6 @@ public class AudioService extends IAudioService.Stub {
private volatile IRingtonePlayer mRingtonePlayer;
private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
- private int mDeviceRotation = Surface.ROTATION_0;
// Request to override default use of A2DP for media.
private boolean mBluetoothA2dpEnabled;
@@ -545,8 +544,6 @@ public class AudioService extends IAudioService.Stub {
// If absolute volume is supported in AVRCP device
private boolean mAvrcpAbsVolSupported = false;
- private AudioOrientationEventListener mOrientationListener;
-
private static Long mLastDeviceConnectMsgTime = new Long(0);
private AudioManagerInternal.RingerModeDelegate mRingerModeDelegate;
@@ -669,15 +666,7 @@ public class AudioService extends IAudioService.Stub {
}
mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
if (mMonitorRotation) {
- mDeviceRotation = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE))
- .getDefaultDisplay().getRotation();
- Log.v(TAG, "monitoring device rotation, initial=" + mDeviceRotation);
-
- mOrientationListener = new AudioOrientationEventListener(mContext);
- mOrientationListener.enable();
-
- // initialize rotation in AudioSystem
- setRotationForAudioSystem();
+ RotationHelper.init(mContext, mAudioHandler);
}
context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
@@ -805,7 +794,7 @@ public class AudioService extends IAudioService.Stub {
setOrientationForAudioSystem();
}
if (mMonitorRotation) {
- setRotationForAudioSystem();
+ RotationHelper.updateOrientation();
}
synchronized (mBluetoothA2dpEnabledLock) {
@@ -1058,25 +1047,6 @@ public class AudioService extends IAudioService.Stub {
return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
}
- private class AudioOrientationEventListener
- extends OrientationEventListener {
- public AudioOrientationEventListener(Context context) {
- super(context);
- }
-
- @Override
- public void onOrientationChanged(int orientation) {
- //Even though we're responding to phone orientation events,
- //use display rotation so audio stays in sync with video/dialogs
- int newRotation = ((WindowManager) mContext.getSystemService(
- Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
- if (newRotation != mDeviceRotation) {
- mDeviceRotation = newRotation;
- setRotationForAudioSystem();
- }
- }
- }
-
///////////////////////////////////////////////////////////////////////////
// IPC methods
///////////////////////////////////////////////////////////////////////////
@@ -5066,14 +5036,13 @@ public class AudioService extends IAudioService.Stub {
}
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
if (mMonitorRotation) {
- mOrientationListener.onOrientationChanged(0); //argument is ignored anyway
- mOrientationListener.enable();
+ RotationHelper.enable();
}
AudioSystem.setParameters("screen_state=on");
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
if (mMonitorRotation) {
//reduce wakeups (save current) by only listening when display is on
- mOrientationListener.disable();
+ RotationHelper.disable();
}
AudioSystem.setParameters("screen_state=off");
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
@@ -5322,6 +5291,7 @@ public class AudioService extends IAudioService.Stub {
}
}
+ //TODO move to an external "orientation helper" class
private void setOrientationForAudioSystem() {
switch (mDeviceOrientation) {
case Configuration.ORIENTATION_LANDSCAPE:
@@ -5345,26 +5315,6 @@ public class AudioService extends IAudioService.Stub {
}
}
- private void setRotationForAudioSystem() {
- switch (mDeviceRotation) {
- case Surface.ROTATION_0:
- AudioSystem.setParameters("rotation=0");
- break;
- case Surface.ROTATION_90:
- AudioSystem.setParameters("rotation=90");
- break;
- case Surface.ROTATION_180:
- AudioSystem.setParameters("rotation=180");
- break;
- case Surface.ROTATION_270:
- AudioSystem.setParameters("rotation=270");
- break;
- default:
- Log.e(TAG, "Unknown device rotation");
- }
- }
-
-
// Handles request to override default use of A2DP for media.
// Must be called synchronized on mConnectedDevices
public void setBluetoothA2dpOnInt(boolean on) {
diff --git a/services/core/java/com/android/server/audio/RotationHelper.java b/services/core/java/com/android/server/audio/RotationHelper.java
new file mode 100644
index 000000000000..359cc360bbc7
--- /dev/null
+++ b/services/core/java/com/android/server/audio/RotationHelper.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.audio;
+
+import android.content.Context;
+import android.media.AudioSystem;
+import android.os.Handler;
+import android.util.Log;
+import android.view.OrientationEventListener;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import com.android.server.policy.WindowOrientationListener;
+
+/**
+ * Class to handle device rotation events for AudioService, and forward device rotation
+ * to the audio HALs through AudioSystem.
+ *
+ * The role of this class is to monitor device orientation changes, and upon rotation,
+ * verify the UI orientation. In case of a change, send the new orientation, in increments
+ * of 90deg, through AudioSystem.
+ *
+ * Note that even though we're responding to device orientation events, we always
+ * query the display rotation so audio stays in sync with video/dialogs. This is
+ * done with .getDefaultDisplay().getRotation() from WINDOW_SERVICE.
+ */
+class RotationHelper {
+
+ private static final String TAG = "AudioService.RotationHelper";
+
+ private static AudioOrientationListener sOrientationListener;
+ private static AudioWindowOrientationListener sWindowOrientationListener;
+
+ private static final Object sRotationLock = new Object();
+ private static int sDeviceRotation = Surface.ROTATION_0; // R/W synchronized on sRotationLock
+
+ private static Context sContext;
+
+ /**
+ * post conditions:
+ * - (sWindowOrientationListener != null) xor (sOrientationListener != null)
+ * - sWindowOrientationListener xor sOrientationListener is enabled
+ * - sContext != null
+ */
+ static void init(Context context, Handler handler) {
+ if (context == null) {
+ throw new IllegalArgumentException("Invalid null context");
+ }
+ sContext = context;
+ sWindowOrientationListener = new AudioWindowOrientationListener(context, handler);
+ sWindowOrientationListener.enable();
+ if (!sWindowOrientationListener.canDetectOrientation()) {
+ // cannot use com.android.server.policy.WindowOrientationListener, revert to public
+ // orientation API
+ Log.i(TAG, "Not using WindowOrientationListener, reverting to OrientationListener");
+ sWindowOrientationListener.disable();
+ sWindowOrientationListener = null;
+ sOrientationListener = new AudioOrientationListener(context);
+ sOrientationListener.enable();
+ }
+ }
+
+ static void enable() {
+ if (sWindowOrientationListener != null) {
+ sWindowOrientationListener.enable();
+ } else {
+ sOrientationListener.enable();
+ }
+ updateOrientation();
+ }
+
+ static void disable() {
+ if (sWindowOrientationListener != null) {
+ sWindowOrientationListener.disable();
+ } else {
+ sOrientationListener.disable();
+ }
+ }
+
+ /**
+ * Query current display rotation and publish the change if any.
+ */
+ static void updateOrientation() {
+ // Even though we're responding to device orientation events,
+ // use display rotation so audio stays in sync with video/dialogs
+ int newRotation = ((WindowManager) sContext.getSystemService(
+ Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
+ synchronized(sRotationLock) {
+ if (newRotation != sDeviceRotation) {
+ sDeviceRotation = newRotation;
+ publishRotation(sDeviceRotation);
+ }
+ }
+ }
+
+ private static void publishRotation(int rotation) {
+ Log.v(TAG, "publishing device rotation =" + rotation + " (x90deg)");
+ switch (rotation) {
+ case Surface.ROTATION_0:
+ AudioSystem.setParameters("rotation=0");
+ break;
+ case Surface.ROTATION_90:
+ AudioSystem.setParameters("rotation=90");
+ break;
+ case Surface.ROTATION_180:
+ AudioSystem.setParameters("rotation=180");
+ break;
+ case Surface.ROTATION_270:
+ AudioSystem.setParameters("rotation=270");
+ break;
+ default:
+ Log.e(TAG, "Unknown device rotation");
+ }
+ }
+
+ /**
+ * Uses android.view.OrientationEventListener
+ */
+ final static class AudioOrientationListener extends OrientationEventListener {
+ AudioOrientationListener(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onOrientationChanged(int orientation) {
+ updateOrientation();
+ }
+ }
+
+ /**
+ * Uses com.android.server.policy.WindowOrientationListener
+ */
+ final static class AudioWindowOrientationListener extends WindowOrientationListener {
+ private static RotationCheckThread sRotationCheckThread;
+
+ AudioWindowOrientationListener(Context context, Handler handler) {
+ super(context, handler);
+ }
+
+ public void onProposedRotationChanged(int rotation) {
+ updateOrientation();
+ if (sRotationCheckThread != null) {
+ sRotationCheckThread.endCheck();
+ }
+ sRotationCheckThread = new RotationCheckThread();
+ sRotationCheckThread.beginCheck();
+ }
+ }
+
+ /**
+ * When com.android.server.policy.WindowOrientationListener report an orientation change,
+ * the UI may not have rotated yet. This thread polls with gradually increasing delays
+ * the new orientation.
+ */
+ final static class RotationCheckThread extends Thread {
+ // how long to wait between each rotation check
+ private final int[] WAIT_TIMES_MS = { 10, 20, 50, 100, 100, 200, 200, 500 };
+ private int mWaitCounter;
+ private final Object mCounterLock = new Object();
+
+ RotationCheckThread() {
+ super("RotationCheck");
+ }
+
+ void beginCheck() {
+ synchronized(mCounterLock) {
+ mWaitCounter = 0;
+ }
+ try {
+ start();
+ } catch (IllegalStateException e) { }
+ }
+
+ void endCheck() {
+ synchronized(mCounterLock) {
+ mWaitCounter = WAIT_TIMES_MS.length;
+ }
+ }
+
+ public void run() {
+ while (mWaitCounter < WAIT_TIMES_MS.length) {
+ int waitTimeMs;
+ synchronized(mCounterLock) {
+ waitTimeMs = mWaitCounter < WAIT_TIMES_MS.length ?
+ WAIT_TIMES_MS[mWaitCounter] : 0;
+ mWaitCounter++;
+ }
+ try {
+ if (waitTimeMs > 0) {
+ sleep(waitTimeMs);
+ updateOrientation();
+ }
+ } catch (InterruptedException e) { }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index ecda36a0d4c6..06bd583fc7b3 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -443,13 +443,16 @@ public class JobSchedulerService extends com.android.server.SystemService
}
/**
- * A job is rescheduled with exponential back-off if the client requests this from their
- * execution logic.
- * A caveat is for idle-mode jobs, for which the idle-mode constraint will usurp the
- * timeliness of the reschedule. For an idle-mode job, no deadline is given.
+ * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
+ * specify an override deadline on a failed job (the failed job will run even though it's not
+ * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
+ * ready job with {@link JobStatus#numFailures} > 0 will be executed.
+ *
* @param failureToReschedule Provided job status that we will reschedule.
* @return A newly instantiated JobStatus with the same constraints as the last job except
* with adjusted timing constraints.
+ *
+ * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
*/
private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
final long elapsedNowMillis = SystemClock.elapsedRealtime();
@@ -479,8 +482,9 @@ public class JobSchedulerService extends com.android.server.SystemService
}
/**
- * Called after a periodic has executed so we can to re-add it. We take the last execution time
- * of the job to be the time of completion (i.e. the time at which this function is called).
+ * Called after a periodic has executed so we can reschedule it. We take the last execution
+ * time of the job to be the time of completion (i.e. the time at which this function is
+ * called).
* This could be inaccurate b/c the job can run for as long as
* {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
* to underscheduling at least, rather than if we had taken the last execution time to be the
@@ -491,7 +495,12 @@ public class JobSchedulerService extends com.android.server.SystemService
private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
final long elapsedNow = SystemClock.elapsedRealtime();
// Compute how much of the period is remaining.
- long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0);
+ long runEarly = 0L;
+
+ // If this periodic was rescheduled it won't have a deadline.
+ if (periodicToReschedule.hasDeadlineConstraint()) {
+ runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
+ }
long newEarliestRunTimeElapsed = elapsedNow + runEarly;
long period = periodicToReschedule.getJob().getIntervalMillis();
long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period;
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index d26319b5b0b9..d7fafe3e570d 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -68,7 +68,7 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne
private static final int defaultMaxActiveJobsPerService =
ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
/** Amount of time a job is allowed to execute for before being considered timed-out. */
- private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000;
+ private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
/** Amount of time the JobScheduler will wait for a response from an app for a message. */
private static final long OP_TIMEOUT_MILLIS = 8 * 1000;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7b15aad7b6ca..e8e46efd55df 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1602,41 +1602,45 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public AutomaticZenRule getAutomaticZenRule(String name) throws RemoteException {
+ public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
+ Preconditions.checkNotNull(id, "Id is null");
enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
- return mZenModeHelper.getAutomaticZenRule(name);
+ return mZenModeHelper.getAutomaticZenRule(id);
}
@Override
- public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule)
+ public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule)
throws RemoteException {
Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
- enforcePolicyAccess(Binder.getCallingUid(), "addOrUpdateZenModeRule");
+ enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
- return mZenModeHelper.addOrUpdateAutomaticZenRule(automaticZenRule,
- "addOrUpdateAutomaticZenRule");
+ return mZenModeHelper.addAutomaticZenRule(automaticZenRule,
+ "addAutomaticZenRule");
}
@Override
- public boolean renameAutomaticZenRule(String oldName, String newName) {
- Preconditions.checkNotNull(oldName, "oldName is null");
- Preconditions.checkNotNull(newName, "newName is null");
- enforcePolicyAccess(Binder.getCallingUid(), "renameAutomaticZenRule");
+ public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule)
+ throws RemoteException {
+ Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
+ Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
+ Preconditions.checkNotNull(automaticZenRule.getOwner(), "Owner is null");
+ Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
+ enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
- return mZenModeHelper.renameAutomaticZenRule(
- oldName, newName, "renameAutomaticZenRule");
+ return mZenModeHelper.updateAutomaticZenRule(automaticZenRule,
+ "updateAutomaticZenRule");
}
@Override
- public boolean removeAutomaticZenRule(String name) throws RemoteException {
- Preconditions.checkNotNull(name, "Name is null");
+ public boolean removeAutomaticZenRule(String id) throws RemoteException {
+ Preconditions.checkNotNull(id, "Id is null");
// Verify that they can modify zen rules.
enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
- return mZenModeHelper.removeAutomaticZenRule(name, "removeAutomaticZenRule");
+ return mZenModeHelper.removeAutomaticZenRule(id, "removeAutomaticZenRule");
}
@Override
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index b89a654323f3..c2e434979c01 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -104,7 +104,7 @@ public class ZenModeConditions implements ConditionProviders.Callback {
public void onServiceAdded(ComponentName component) {
if (DEBUG) Log.d(TAG, "onServiceAdded " + component);
if (isAutomaticActive(component)) {
- mHelper.setConfig(mHelper.getConfig(), "zmc.onServiceAdded");
+ mHelper.setConfigAsync(mHelper.getConfig(), "zmc.onServiceAdded");
}
}
@@ -120,7 +120,7 @@ public class ZenModeConditions implements ConditionProviders.Callback {
updated |= updateSnoozing(automaticRule);
}
if (updated) {
- mHelper.setConfig(config, "conditionChanged");
+ mHelper.setConfigAsync(config, "conditionChanged");
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4d41e3a9d734..76c64432e54b 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -49,6 +49,7 @@ import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.EventInfo;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.service.notification.ZenModeConfig.ZenRule;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
@@ -211,82 +212,69 @@ public class ZenModeHelper {
return rules;
}
- public AutomaticZenRule getAutomaticZenRule(String name) {
+ public AutomaticZenRule getAutomaticZenRule(String id) {
if (mConfig == null) return null;
- for(ZenRule rule : mConfig.automaticRules.values()) {
- if (canManageAutomaticZenRule(rule) && rule.name.equals(name)) {
- return createAutomaticZenRule(rule);
- }
+ ZenRule rule = mConfig.automaticRules.get(id);
+ if (rule == null) return null;
+ if (canManageAutomaticZenRule(rule)) {
+ return createAutomaticZenRule(rule);
}
return null;
}
- public boolean addOrUpdateAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
- if (mConfig == null) return false;
+ public AutomaticZenRule addAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
+ if (mConfig == null) return null;
if (DEBUG) {
- Log.d(TAG, "addOrUpdateAutomaticZenRule zenRule=" + automaticZenRule
- + " reason=" + reason);
+ Log.d(TAG, "addAutomaticZenRule zenRule= " + automaticZenRule + " reason=" +reason);
+ }
+ if (!TextUtils.isEmpty(automaticZenRule.getId())) {
+ throw new IllegalArgumentException("Rule already exists");
}
final ZenModeConfig newConfig = mConfig.copy();
- String ruleId = findMatchingRuleId(newConfig, automaticZenRule.getName());
- ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
- if (ruleId == null) {
- ruleId = newConfig.newRuleId();
- rule.name = automaticZenRule.getName();
- rule.component = automaticZenRule.getOwner();
+ ZenRule rule = new ZenRule();
+ populateZenRule(automaticZenRule, rule, true);
+ newConfig.automaticRules.put(rule.id, rule);
+ if (setConfig(newConfig, reason, true)) {
+ return createAutomaticZenRule(rule);
} else {
- rule = newConfig.automaticRules.get(ruleId);
- if (!canManageAutomaticZenRule(rule)) {
- throw new SecurityException(
- "Cannot update rules not owned by your condition provider");
- }
- }
- if (rule.enabled != automaticZenRule.isEnabled()) {
- rule.snoozing = false;
+ return null;
}
- rule.condition = null;
- rule.conditionId = automaticZenRule.getConditionId();
- rule.enabled = automaticZenRule.isEnabled();
- rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
- automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
- newConfig.automaticRules.put(ruleId, rule);
- return setConfig(newConfig, reason, true);
}
- public boolean renameAutomaticZenRule(String oldName, String newName, String reason) {
+ public boolean updateAutomaticZenRule(AutomaticZenRule automaticZenRule, String reason) {
if (mConfig == null) return false;
if (DEBUG) {
- Log.d(TAG, "renameAutomaticZenRule oldName=" + oldName + " newName=" + newName
+ Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
+ " reason=" + reason);
}
final ZenModeConfig newConfig = mConfig.copy();
- String ruleId = findMatchingRuleId(newConfig, oldName);
+ final String ruleId = automaticZenRule.getId();
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
if (ruleId == null) {
- return false;
+ throw new IllegalArgumentException("Rule doesn't exist");
} else {
- ZenRule rule = newConfig.automaticRules.get(ruleId);
- if (!canManageAutomaticZenRule(rule)) {
+ rule = newConfig.automaticRules.get(ruleId);
+ if (rule == null || !canManageAutomaticZenRule(rule)) {
throw new SecurityException(
"Cannot update rules not owned by your condition provider");
}
- rule.name = newName;
- return setConfig(newConfig, reason, true);
}
+ populateZenRule(automaticZenRule, rule, false);
+ newConfig.automaticRules.put(ruleId, rule);
+ return setConfig(newConfig, reason, true);
}
- public boolean removeAutomaticZenRule(String name, String reason) {
+ public boolean removeAutomaticZenRule(String id, String reason) {
if (mConfig == null) return false;
final ZenModeConfig newConfig = mConfig.copy();
- String ruleId = findMatchingRuleId(newConfig, name);
- if (ruleId != null) {
- ZenRule rule = newConfig.automaticRules.get(ruleId);
- if (canManageAutomaticZenRule(rule)) {
- newConfig.automaticRules.remove(ruleId);
- if (DEBUG) Log.d(TAG, "removeZenRule zenRule=" + name + " reason=" + reason);
- } else {
- throw new SecurityException(
- "Cannot delete rules not owned by your condition provider");
- }
+ ZenRule rule = newConfig.automaticRules.get(id);
+ if (rule == null) return false;
+ if (canManageAutomaticZenRule(rule)) {
+ newConfig.automaticRules.remove(id);
+ if (DEBUG) Log.d(TAG, "removeZenRule zenRule=" + id + " reason=" + reason);
+ } else {
+ throw new SecurityException(
+ "Cannot delete rules not owned by your condition provider");
}
return setConfig(newConfig, reason, true);
}
@@ -310,9 +298,28 @@ public class ZenModeHelper {
}
}
+ private void populateZenRule(AutomaticZenRule automaticZenRule, ZenRule rule, boolean isNew) {
+ if (isNew) {
+ rule.id = ZenModeConfig.newRuleId();
+ rule.creationTime = System.currentTimeMillis();
+ rule.component = automaticZenRule.getOwner();
+ }
+
+ if (rule.enabled != automaticZenRule.isEnabled()) {
+ rule.snoozing = false;
+ }
+ rule.name = automaticZenRule.getName();
+ rule.condition = null;
+ rule.conditionId = automaticZenRule.getConditionId();
+ rule.enabled = automaticZenRule.isEnabled();
+ rule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
+ automaticZenRule.getInterruptionFilter(), Global.ZEN_MODE_OFF);
+ }
+
private AutomaticZenRule createAutomaticZenRule(ZenRule rule) {
return new AutomaticZenRule(rule.name, rule.component, rule.conditionId,
- NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled);
+ NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled,
+ rule.id, rule.creationTime);
}
public void setManualZenMode(int zenMode, Uri conditionId, String reason) {
@@ -344,15 +351,6 @@ public class ZenModeHelper {
setConfig(newConfig, reason, setRingerMode);
}
- private String findMatchingRuleId(ZenModeConfig config, String ruleName) {
- for (String ruleId : config.automaticRules.keySet()) {
- if (config.automaticRules.get(ruleId).name.equals(ruleName)) {
- return ruleId;
- }
- }
- return null;
- }
-
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("mZenMode=");
pw.println(Global.zenModeToString(mZenMode));
@@ -446,6 +444,10 @@ public class ZenModeHelper {
return setConfig(config, reason, true /*setRingerMode*/);
}
+ public void setConfigAsync(ZenModeConfig config, String reason) {
+ mHandler.postSetConfig(config, reason);
+ }
+
private boolean setConfig(ZenModeConfig config, String reason, boolean setRingerMode) {
final long identity = Binder.clearCallingIdentity();
try {
@@ -640,7 +642,9 @@ public class ZenModeHelper {
rule1.conditionId = ZenModeConfig.toScheduleConditionId(weeknights);
rule1.zenMode = Global.ZEN_MODE_ALARMS;
rule1.component = ScheduleConditionProvider.COMPONENT;
- config.automaticRules.put(config.newRuleId(), rule1);
+ rule1.id = ZenModeConfig.newRuleId();
+ rule1.creationTime = System.currentTimeMillis();
+ config.automaticRules.put(rule1.id, rule1);
final ScheduleInfo weekends = new ScheduleInfo();
weekends.days = ZenModeConfig.WEEKEND_DAYS;
@@ -654,7 +658,9 @@ public class ZenModeHelper {
rule2.conditionId = ZenModeConfig.toScheduleConditionId(weekends);
rule2.zenMode = Global.ZEN_MODE_ALARMS;
rule2.component = ScheduleConditionProvider.COMPONENT;
- config.automaticRules.put(config.newRuleId(), rule2);
+ rule2.id = ZenModeConfig.newRuleId();
+ rule2.creationTime = System.currentTimeMillis();
+ config.automaticRules.put(rule2.id, rule2);
}
private void appendDefaultEventRules(ZenModeConfig config) {
@@ -669,7 +675,9 @@ public class ZenModeHelper {
rule.conditionId = ZenModeConfig.toEventConditionId(events);
rule.zenMode = Global.ZEN_MODE_ALARMS;
rule.component = EventConditionProvider.COMPONENT;
- config.automaticRules.put(config.newRuleId(), rule);
+ rule.id = ZenModeConfig.newRuleId();
+ rule.creationTime = System.currentTimeMillis();
+ config.automaticRules.put(rule.id, rule);
}
private static int zenSeverity(int zen) {
@@ -710,7 +718,7 @@ public class ZenModeHelper {
rule.zenMode = v1.sleepNone ? Global.ZEN_MODE_NO_INTERRUPTIONS
: Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
rule.component = ScheduleConditionProvider.COMPONENT;
- rt.automaticRules.put(rt.newRuleId(), rule);
+ rt.automaticRules.put(ZenModeConfig.newRuleId(), rule);
} else {
Log.i(TAG, "No existing V1 downtime found, generating default schedules");
appendDefaultScheduleRules(rt);
@@ -883,6 +891,17 @@ public class ZenModeHelper {
private final class H extends Handler {
private static final int MSG_DISPATCH = 1;
private static final int MSG_METRICS = 2;
+ private static final int MSG_SET_CONFIG = 3;
+
+ private final class ConfigMessageData {
+ public final ZenModeConfig config;
+ public final String reason;
+
+ ConfigMessageData(ZenModeConfig config, String reason) {
+ this.config = config;
+ this.reason = reason;
+ }
+ }
private static final long METRICS_PERIOD_MS = 6 * 60 * 60 * 1000;
@@ -900,6 +919,10 @@ public class ZenModeHelper {
sendEmptyMessageDelayed(MSG_METRICS, METRICS_PERIOD_MS);
}
+ private void postSetConfig(ZenModeConfig config, String reason) {
+ sendMessage(obtainMessage(MSG_SET_CONFIG, new ConfigMessageData(config, reason)));
+ }
+
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -909,6 +932,10 @@ public class ZenModeHelper {
case MSG_METRICS:
mMetrics.emit();
break;
+ case MSG_SET_CONFIG:
+ ConfigMessageData configData = (ConfigMessageData)msg.obj;
+ setConfig(configData.config, configData.reason);
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0366fff4eee9..cf09b846ad4e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -140,6 +140,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
private static final String ATTR_APP_ICON = "appIcon";
private static final String ATTR_APP_LABEL = "appLabel";
private static final String ATTR_ORIGINATING_URI = "originatingUri";
+ private static final String ATTR_ORIGINATING_UID = "originatingUid";
private static final String ATTR_REFERRER_URI = "referrerUri";
private static final String ATTR_ABI_OVERRIDE = "abiOverride";
private static final String ATTR_VOLUME_UUID = "volumeUuid";
@@ -405,6 +406,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
+ params.originatingUid =
+ readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
@@ -477,6 +480,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
+ writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4a473fd42741..6e32e5c575f5 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -222,11 +222,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// waived if the installer is the device owner.
DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
Context.DEVICE_POLICY_SERVICE);
+ final boolean isPermissionGranted =
+ (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
+ == PackageManager.PERMISSION_GRANTED);
+ final boolean isInstallerRoot = (installerUid == Process.ROOT_UID);
+ final boolean forcePermissionPrompt =
+ (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerApp(installerPackageName);
- if ((mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
- == PackageManager.PERMISSION_GRANTED)
- || (installerUid == Process.ROOT_UID)
- || mIsInstallerDeviceOwner) {
+ if ((isPermissionGranted
+ || isInstallerRoot
+ || mIsInstallerDeviceOwner)
+ && !forcePermissionPrompt) {
mPermissionsAccepted = true;
} else {
mPermissionsAccepted = false;
@@ -940,7 +946,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
- UserHandle.USER_OWNER);
+ UserHandle.USER_SYSTEM);
final int gid = UserHandle.getSharedAppGid(uid);
if (!PackageHelper.fixSdPermissions(cid, gid, null)) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
@@ -955,7 +961,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (accepted) {
// Mark and kick off another install pass
- mPermissionsAccepted = true;
+ synchronized (mLock) {
+ mPermissionsAccepted = true;
+ }
mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
} else {
destroyInternal();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c729e28dae82..3e27c954ac42 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1108,7 +1108,7 @@ public class PackageManagerService extends IPackageManager.Stub {
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
if (mContext.bindServiceAsUser(service, mDefContainerConn,
- Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
+ Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;
return true;
@@ -1715,7 +1715,7 @@ public class PackageManagerService extends IPackageManager.Stub {
for (PackageSetting ps : packages) {
Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten");
deletePackage(ps.name, new LegacyPackageDeleteObserver(null).getBinder(),
- UserHandle.USER_OWNER, PackageManager.DELETE_ALL_USERS);
+ UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS);
}
mSettings.onVolumeForgotten(fsUuid);
@@ -1726,7 +1726,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int userId,
String[] grantedPermissions) {
- if (userId >= UserHandle.USER_OWNER) {
+ if (userId >= UserHandle.USER_SYSTEM) {
grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions);
} else if (userId == UserHandle.USER_ALL) {
final int[] userIds;
@@ -2276,7 +2276,7 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.enableSystemPackageLPw(packageName);
try {
- scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, null);
+ scanPackageTracedLI(scanFile, reparseFlags, scanFlags, 0, UserHandle.SYSTEM);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse original system package: "
+ e.getMessage());
@@ -2401,8 +2401,9 @@ public class PackageManagerService extends IPackageManager.Stub {
private String getRequiredVerifierLPr() {
final Intent verification = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
+ // We only care about verifier that's installed under system user.
final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
- PackageManager.GET_DISABLED_COMPONENTS, 0 /* TODO: Which userId? */);
+ PackageManager.GET_DISABLED_COMPONENTS, UserHandle.USER_SYSTEM);
String requiredVerifier = null;
@@ -2417,7 +2418,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final String packageName = info.activityInfo.packageName;
if (checkPermission(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
- packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
+ packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
continue;
}
@@ -2437,7 +2438,7 @@ public class PackageManagerService extends IPackageManager.Stub {
installerIntent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
final List<ResolveInfo> installers = queryIntentActivities(installerIntent,
- PACKAGE_MIME_TYPE, 0, 0);
+ PACKAGE_MIME_TYPE, 0, UserHandle.USER_SYSTEM);
String requiredInstaller = null;
@@ -2467,7 +2468,7 @@ public class PackageManagerService extends IPackageManager.Stub {
private ComponentName getIntentFilterVerifierComponentNameLPr() {
final Intent verification = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
- PackageManager.GET_DISABLED_COMPONENTS, 0 /* userId */);
+ PackageManager.GET_DISABLED_COMPONENTS, UserHandle.USER_SYSTEM);
ComponentName verifierComponentName = null;
@@ -2488,7 +2489,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
- packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
+ packageName, UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
continue;
}
@@ -2784,8 +2785,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if((ps == null) || (ps.pkg == null) || (ps.pkg.applicationInfo == null)) {
return -1;
}
- p = ps.pkg;
- return p != null ? UserHandle.getUid(userId, p.applicationInfo.uid) : -1;
+ return UserHandle.getUid(userId, ps.pkg.applicationInfo.uid);
}
}
@@ -5690,7 +5690,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
try {
scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
- scanFlags, currentTime, null);
+ scanFlags, currentTime, UserHandle.SYSTEM);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
@@ -5796,6 +5796,8 @@ public class PackageManagerService extends IPackageManager.Stub {
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
+ Preconditions.checkNotNull(user);
+
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
parseFlags |= mDefParseFlags;
PackageParser pp = new PackageParser();
@@ -5837,7 +5839,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
boolean updatedPkgBetter = false;
// First check if this is a system package that may involve an update
- if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
+ if (updatedPkg != null && (parseFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
// If new package is not located in "/system/priv-app" (e.g. due to an OTA),
// it needs to drop FLAG_PRIVILEGED.
if (locationIsPrivileged(scanFile)) {
@@ -6932,7 +6934,7 @@ public class PackageManagerService extends IPackageManager.Stub {
} else {
// This is a normal package, need to make its data directory.
dataPath = Environment.getDataUserPackageDirectory(pkg.volumeUuid,
- UserHandle.USER_OWNER, pkg.packageName);
+ UserHandle.USER_SYSTEM, pkg.packageName);
boolean uidError = false;
if (dataPath.exists()) {
@@ -7090,7 +7092,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// if they already exist
if (!TextUtils.isEmpty(pkg.volumeUuid)) {
for (int userId : userIds) {
- if (userId != 0) {
+ if (userId != UserHandle.USER_SYSTEM) {
mInstaller.createUserData(pkg.volumeUuid, pkg.packageName,
UserHandle.getUid(userId, pkg.applicationInfo.uid), userId,
pkg.applicationInfo.seinfo);
@@ -7218,7 +7220,6 @@ public class PackageManagerService extends IPackageManager.Stub {
for (int j=0; j<sysPs.pkg.libraryNames.size(); j++) {
if (name.equals(sysPs.pkg.libraryNames.get(j))) {
allowed = true;
- allowed = true;
break;
}
}
@@ -9603,7 +9604,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (am != null) {
try {
am.startService(null, intent, null, mContext.getOpPackageName(),
- UserHandle.USER_OWNER);
+ UserHandle.USER_SYSTEM);
} catch (RemoteException e) {
}
}
@@ -9686,7 +9687,8 @@ public class PackageManagerService extends IPackageManager.Stub {
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user) {
final VerificationParams verifParams = new VerificationParams(
- null, sessionParams.originatingUri, sessionParams.referrerUri, installerUid, null);
+ null, sessionParams.originatingUri, sessionParams.referrerUri,
+ sessionParams.originatingUid, null);
verifParams.setInstallerUid(installerUid);
final OriginInfo origin;
@@ -10321,7 +10323,8 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " to BM for possible restore");
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
try {
- if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {
+ // TODO: http://b/22388012
+ if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
} else {
doRestore = false;
@@ -10802,14 +10805,11 @@ public class PackageManagerService extends IPackageManager.Stub {
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
- /*
- * ADB installs appear as UserHandle.USER_ALL, and can only be performed by
- * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
- */
- int userIdentifier = getUser().getIdentifier();
- if (userIdentifier == UserHandle.USER_ALL
- && ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) {
- userIdentifier = UserHandle.USER_OWNER;
+ // TODO: http://b/22976637
+ // Apps installed for "all" users use the device owner to verify the app
+ UserHandle verifierUser = getUser();
+ if (verifierUser == UserHandle.ALL) {
+ verifierUser = UserHandle.SYSTEM;
}
/*
@@ -10817,9 +10817,9 @@ public class PackageManagerService extends IPackageManager.Stub {
* do, then we'll defer to them to verify the packages.
*/
final int requiredUid = mRequiredVerifierPackage == null ? -1
- : getPackageUid(mRequiredVerifierPackage, userIdentifier);
+ : getPackageUid(mRequiredVerifierPackage, verifierUser.getIdentifier());
if (!origin.existing && requiredUid != -1
- && isVerificationEnabled(userIdentifier, installFlags)) {
+ && isVerificationEnabled(verifierUser.getIdentifier(), installFlags)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -10829,7 +10829,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final List<ResolveInfo> receivers = queryIntentReceivers(verification,
PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS,
- 0 /* TODO: Which userId? */);
+ verifierUser.getIdentifier());
if (DEBUG_VERIFY) {
Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
@@ -10884,12 +10884,6 @@ public class PackageManagerService extends IPackageManager.Stub {
final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
receivers, verificationState);
- // Apps installed for "all" users use the device owner to verify the app
- UserHandle verifierUser = getUser();
- if (verifierUser == UserHandle.ALL) {
- verifierUser = UserHandle.OWNER;
- }
-
/*
* If any sufficient verifiers were listed in the package
* manifest, attempt to ask them.
@@ -12120,7 +12114,8 @@ public class PackageManagerService extends IPackageManager.Stub {
(oldExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
int oldScanFlags = SCAN_UPDATE_SIGNATURE | SCAN_UPDATE_TIME;
try {
- scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime, null);
+ scanPackageTracedLI(restoreFile, oldParseFlags, oldScanFlags, origUpdateTime,
+ UserHandle.SYSTEM);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to restore package : " + pkgName + " after failed upgrade: "
+ e.getMessage());
@@ -12615,7 +12610,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final int verifierUid = getPackageUid(
mIntentFilterVerifierComponent.getPackageName(),
- (userId == UserHandle.USER_ALL) ? UserHandle.USER_OWNER : userId);
+ (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
mHandler.removeMessages(START_INTENT_FILTER_VERIFICATIONS);
final Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
@@ -12815,11 +12810,14 @@ public class PackageManagerService extends IPackageManager.Stub {
Preconditions.checkNotNull(packageName);
Preconditions.checkNotNull(observer);
final int uid = Binder.getCallingUid();
- if (UserHandle.getUserId(uid) != userId) {
+ final boolean deleteAllUsers = (flags & PackageManager.DELETE_ALL_USERS) != 0;
+ final int[] users = deleteAllUsers ? sUserManager.getUserIds() : new int[]{ userId };
+ if (UserHandle.getUserId(uid) != userId || (deleteAllUsers && users.length > 1)) {
mContext.enforceCallingPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
"deletePackage for user " + userId);
}
+
if (isUserRestricted(userId, UserManager.DISALLOW_UNINSTALL_APPS)) {
try {
observer.onPackageDeleted(packageName,
@@ -12829,25 +12827,15 @@ public class PackageManagerService extends IPackageManager.Stub {
return;
}
- boolean uninstallBlocked = false;
- if ((flags & PackageManager.DELETE_ALL_USERS) != 0) {
- int[] users = sUserManager.getUserIds();
- for (int i = 0; i < users.length; ++i) {
- if (getBlockUninstallForUser(packageName, users[i])) {
- uninstallBlocked = true;
- break;
+ for (int currentUserId : users) {
+ if (getBlockUninstallForUser(packageName, currentUserId)) {
+ try {
+ observer.onPackageDeleted(packageName,
+ PackageManager.DELETE_FAILED_OWNER_BLOCKED, null);
+ } catch (RemoteException re) {
}
+ return;
}
- } else {
- uninstallBlocked = getBlockUninstallForUser(packageName, userId);
- }
- if (uninstallBlocked) {
- try {
- observer.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_OWNER_BLOCKED,
- null);
- } catch (RemoteException re) {
- }
- return;
}
if (DEBUG_REMOVE) {
@@ -12858,13 +12846,11 @@ public class PackageManagerService extends IPackageManager.Stub {
public void run() {
mHandler.removeCallbacks(this);
final int returnCode = deletePackageX(packageName, userId, flags);
- if (observer != null) {
- try {
- observer.onPackageDeleted(packageName, returnCode, null);
- } catch (RemoteException e) {
- Log.i(TAG, "Observer no longer exists.");
- } //end catch
- } //end if
+ try {
+ observer.onPackageDeleted(packageName, returnCode, null);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Observer no longer exists.");
+ } //end catch
} //end run
});
}
@@ -12874,9 +12860,11 @@ public class PackageManagerService extends IPackageManager.Stub {
ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
try {
if (dpm != null) {
- if (dpm.isDeviceOwner(packageName)) {
+ // Does the package contains the device owner?
+ if (dpm.isDeviceOwnerPackage(packageName)) {
return true;
}
+ // Does it contain a device admin for any user?
int[] users;
if (userId == UserHandle.USER_ALL) {
users = sUserManager.getUserIds();
@@ -13062,7 +13050,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs,
userId);
if (userIdToKill == UserHandle.USER_ALL
- || userIdToKill >= UserHandle.USER_OWNER) {
+ || userIdToKill >= UserHandle.USER_SYSTEM) {
// If gids changed for this user, kill all affected packages.
mHandler.post(new Runnable() {
@Override
@@ -13179,7 +13167,8 @@ public class PackageManagerService extends IPackageManager.Stub {
final PackageParser.Package newPkg;
try {
- newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0, null);
+ newPkg = scanPackageTracedLI(disabledPs.codePath, parseFlags, SCAN_NO_PATHS, 0,
+ UserHandle.SYSTEM);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to restore system package:" + newPs.name + ": " + e.getMessage());
return false;
@@ -13430,7 +13419,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
final ClearStorageConnection conn = new ClearStorageConnection();
if (mContext.bindServiceAsUser(
- containerIntent, conn, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
+ containerIntent, conn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
try {
for (int curUser : users) {
long timeout = SystemClock.uptimeMillis() + 5000;
@@ -15742,7 +15731,8 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mInstallLock) {
PackageParser.Package pkg = null;
try {
- pkg = scanPackageTracedLI(new File(codePath), parseFlags, 0, 0, null);
+ pkg = scanPackageTracedLI(new File(codePath), parseFlags, 0, 0,
+ UserHandle.SYSTEM);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to scan " + codePath + ": " + e.getMessage());
}
@@ -15902,7 +15892,8 @@ public class PackageManagerService extends IPackageManager.Stub {
synchronized (mInstallLock) {
final PackageParser.Package pkg;
try {
- pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0L, null);
+ pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0,
+ UserHandle.SYSTEM);
loaded.add(pkg.applicationInfo);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index de106a1d0d3c..341410d3522d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -20,6 +20,7 @@ import android.accounts.Account;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
import android.app.IStopUserCallback;
import android.app.admin.DevicePolicyManager;
@@ -58,13 +59,11 @@ import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.util.Xml;
-import com.google.android.collect.Sets;
-
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
-import com.android.server.accounts.AccountManagerService;
+import com.android.server.LocalServices;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -81,7 +80,6 @@ import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
import libcore.io.IoUtils;
@@ -130,6 +128,8 @@ public class UserManagerService extends IUserManager.Stub {
private static final String XML_SUFFIX = ".xml";
private static final int MIN_USER_ID = 10;
+ // We need to keep process uid within Integer.MAX_VALUE.
+ private static final int MAX_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE;
private static final int USER_VERSION = 6;
@@ -144,10 +144,6 @@ public class UserManagerService extends IUserManager.Stub {
*/
private static final boolean CONFIG_PROFILES_SHARE_CREDENTIAL = true;
- // Set of user restrictions, which can only be enforced by the system
- private static final Set<String> SYSTEM_CONTROLLED_RESTRICTIONS = Sets.newArraySet(
- UserManager.DISALLOW_RECORD_AUDIO);
-
static final int WRITE_USER_MSG = 1;
static final int WRITE_USER_DELAY = 2*1000; // 2 seconds
@@ -242,13 +238,15 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mPackagesLock) {
// Prune out any partially created/partially removed users.
ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
- for (int i = 0; i < mUsers.size(); i++) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i);
if ((ui.partial || ui.guestToRemove) && i != 0) {
partials.add(ui);
}
}
- for (int i = 0; i < partials.size(); i++) {
+ final int partialsSize = partials.size();
+ for (int i = 0; i < partialsSize; i++) {
UserInfo ui = partials.get(i);
Slog.w(LOG_TAG, "Removing partially created user " + ui.id
+ " (name=" + ui.name + ")");
@@ -272,7 +270,8 @@ public class UserManagerService extends IUserManager.Stub {
public UserInfo getPrimaryUser() {
checkManageUsersPermission("query users");
synchronized (mPackagesLock) {
- for (int i = 0; i < mUsers.size(); i++) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i);
if (ui.isPrimary()) {
return ui;
@@ -287,7 +286,8 @@ public class UserManagerService extends IUserManager.Stub {
checkManageUsersPermission("query users");
synchronized (mPackagesLock) {
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
- for (int i = 0; i < mUsers.size(); i++) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
UserInfo ui = mUsers.valueAt(i);
if (ui.partial) {
continue;
@@ -323,7 +323,8 @@ public class UserManagerService extends IUserManager.Stub {
// Probably a dying user
return users;
}
- for (int i = 0; i < mUsers.size(); i++) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
UserInfo profile = mUsers.valueAt(i);
if (!isProfileOf(user, profile)) {
continue;
@@ -588,7 +589,7 @@ public class UserManagerService extends IUserManager.Stub {
public void setUserRestriction(String key, boolean value, int userId) {
checkManageUsersPermission("setUserRestriction");
synchronized (mPackagesLock) {
- if (!SYSTEM_CONTROLLED_RESTRICTIONS.contains(key)) {
+ if (!UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS.contains(key)) {
Bundle restrictions = getUserRestrictions(userId);
restrictions.putBoolean(key, value);
setUserRestrictionsInternalLocked(restrictions, userId);
@@ -614,7 +615,7 @@ public class UserManagerService extends IUserManager.Stub {
synchronized (mPackagesLock) {
final Bundle oldUserRestrictions = mUserRestrictions.get(userId);
// Restore the original state of system controlled restrictions from oldUserRestrictions
- for (String key : SYSTEM_CONTROLLED_RESTRICTIONS) {
+ for (String key : UserRestrictionsUtils.SYSTEM_CONTROLLED_USER_RESTRICTIONS) {
restrictions.remove(key);
if (oldUserRestrictions.containsKey(key)) {
restrictions.putBoolean(key, oldUserRestrictions.getBoolean(key));
@@ -807,7 +808,8 @@ public class UserManagerService extends IUserManager.Stub {
&& type != XmlPullParser.END_TAG) {
if (type == XmlPullParser.START_TAG) {
if (parser.getName().equals(TAG_RESTRICTIONS)) {
- readRestrictionsLocked(parser, mGuestRestrictions);
+ UserRestrictionsUtils
+ .readRestrictions(parser, mGuestRestrictions);
}
break;
}
@@ -838,7 +840,7 @@ public class UserManagerService extends IUserManager.Stub {
int userVersion = mUserVersion;
if (userVersion < 1) {
// Assign a proper name for the owner, if not initialized correctly before
- UserInfo user = mUsers.get(UserHandle.USER_OWNER);
+ UserInfo user = mUsers.get(UserHandle.USER_SYSTEM);
if ("Primary".equals(user.name)) {
user.name = mContext.getResources().getString(com.android.internal.R.string.owner_name);
scheduleWriteUserLocked(user);
@@ -848,7 +850,7 @@ public class UserManagerService extends IUserManager.Stub {
if (userVersion < 2) {
// Owner should be marked as initialized
- UserInfo user = mUsers.get(UserHandle.USER_OWNER);
+ UserInfo user = mUsers.get(UserHandle.USER_SYSTEM);
if ((user.flags & UserInfo.FLAG_INITIALIZED) == 0) {
user.flags |= UserInfo.FLAG_INITIALIZED;
scheduleWriteUserLocked(user);
@@ -970,7 +972,7 @@ public class UserManagerService extends IUserManager.Stub {
serializer.endTag(null, TAG_NAME);
Bundle restrictions = mUserRestrictions.get(userInfo.id);
if (restrictions != null) {
- writeRestrictionsLocked(serializer, restrictions);
+ UserRestrictionsUtils.writeRestrictions(serializer, restrictions, TAG_RESTRICTIONS);
}
serializer.endTag(null, TAG_USER);
@@ -1008,9 +1010,11 @@ public class UserManagerService extends IUserManager.Stub {
serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion));
serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
- writeRestrictionsLocked(serializer, mGuestRestrictions);
+ UserRestrictionsUtils
+ .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
- for (int i = 0; i < mUsers.size(); i++) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
UserInfo user = mUsers.valueAt(i);
serializer.startTag(null, TAG_USER);
serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
@@ -1027,45 +1031,6 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- private void writeRestrictionsLocked(XmlSerializer serializer, Bundle restrictions)
- throws IOException {
- serializer.startTag(null, TAG_RESTRICTIONS);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_INSTALL_APPS);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
- writeBoolean(serializer, restrictions,
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_REMOVE_USER);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_VPN);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_TETHERING);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_NETWORK_RESET);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_FACTORY_RESET);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADD_USER);
- writeBoolean(serializer, restrictions, UserManager.ENSURE_VERIFY_APPS);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_APPS_CONTROL);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_SMS);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_FUN);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_BEAM);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_WALLPAPER);
- writeBoolean(serializer, restrictions, UserManager.DISALLOW_SAFE_BOOT);
- writeBoolean(serializer, restrictions, UserManager.ALLOW_PARENT_PROFILE_APP_LINKING);
- serializer.endTag(null, TAG_RESTRICTIONS);
- }
-
private UserInfo readUserLocked(int id) {
int flags = 0;
int serialNumber = id;
@@ -1134,7 +1099,7 @@ public class UserManagerService extends IUserManager.Stub {
name = parser.getText();
}
} else if (TAG_RESTRICTIONS.equals(tag)) {
- readRestrictionsLocked(parser, restrictions);
+ UserRestrictionsUtils.readRestrictions(parser, restrictions);
}
}
}
@@ -1163,60 +1128,6 @@ public class UserManagerService extends IUserManager.Stub {
return null;
}
- private void readRestrictionsLocked(XmlPullParser parser, Bundle restrictions)
- throws IOException {
- readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
- readBoolean(parser, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
- readBoolean(parser, restrictions, UserManager.DISALLOW_INSTALL_APPS);
- readBoolean(parser, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
- readBoolean(parser, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
- readBoolean(parser, restrictions,
- UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
- readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
- readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
- readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS);
- readBoolean(parser, restrictions, UserManager.DISALLOW_REMOVE_USER);
- readBoolean(parser, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES);
- readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_VPN);
- readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_TETHERING);
- readBoolean(parser, restrictions, UserManager.DISALLOW_NETWORK_RESET);
- readBoolean(parser, restrictions, UserManager.DISALLOW_FACTORY_RESET);
- readBoolean(parser, restrictions, UserManager.DISALLOW_ADD_USER);
- readBoolean(parser, restrictions, UserManager.ENSURE_VERIFY_APPS);
- readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
- readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
- readBoolean(parser, restrictions, UserManager.DISALLOW_APPS_CONTROL);
- readBoolean(parser, restrictions,
- UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
- readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
- readBoolean(parser, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
- readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
- readBoolean(parser, restrictions, UserManager.DISALLOW_SMS);
- readBoolean(parser, restrictions, UserManager.DISALLOW_FUN);
- readBoolean(parser, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
- readBoolean(parser, restrictions, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
- readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_BEAM);
- readBoolean(parser, restrictions, UserManager.DISALLOW_WALLPAPER);
- readBoolean(parser, restrictions, UserManager.DISALLOW_SAFE_BOOT);
- readBoolean(parser, restrictions, UserManager.ALLOW_PARENT_PROFILE_APP_LINKING);
- }
-
- private void readBoolean(XmlPullParser parser, Bundle restrictions,
- String restrictionKey) {
- String value = parser.getAttributeValue(null, restrictionKey);
- if (value != null) {
- restrictions.putBoolean(restrictionKey, Boolean.parseBoolean(value));
- }
- }
-
- private void writeBoolean(XmlSerializer xml, Bundle restrictions, String restrictionKey)
- throws IOException {
- if (restrictions.containsKey(restrictionKey)) {
- xml.attribute(null, restrictionKey,
- Boolean.toString(restrictions.getBoolean(restrictionKey)));
- }
- }
-
private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) {
String valueString = parser.getAttributeValue(null, attr);
if (valueString == null) return defaultValue;
@@ -1511,6 +1422,11 @@ public class UserManagerService extends IUserManager.Stub {
long ident = Binder.clearCallingIdentity();
try {
final UserInfo user;
+ int currentUser = ActivityManager.getCurrentUser();
+ if (currentUser == userHandle) {
+ Log.w(LOG_TAG, "Current user cannot be removed");
+ return false;
+ }
synchronized (mPackagesLock) {
user = mUsers.get(userHandle);
if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
@@ -1587,6 +1503,9 @@ public class UserManagerService extends IUserManager.Stub {
}
new Thread() {
public void run() {
+ // Clean up any ActivityManager state
+ LocalServices.getService(ActivityManagerInternal.class)
+ .onUserRemoved(userHandle);
synchronized (mInstallLock) {
synchronized (mPackagesLock) {
removeUserStateLocked(userHandle);
@@ -1951,14 +1870,15 @@ public class UserManagerService extends IUserManager.Stub {
*/
private void updateUserIdsLocked() {
int num = 0;
- for (int i = 0; i < mUsers.size(); i++) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
if (!mUsers.valueAt(i).partial) {
num++;
}
}
final int[] newUsers = new int[num];
int n = 0;
- for (int i = 0; i < mUsers.size(); i++) {
+ for (int i = 0; i < userSize; i++) {
if (!mUsers.valueAt(i).partial) {
newUsers[n++] = mUsers.keyAt(i);
}
@@ -1994,14 +1914,14 @@ public class UserManagerService extends IUserManager.Stub {
private int getNextAvailableIdLocked() {
synchronized (mPackagesLock) {
int i = MIN_USER_ID;
- while (i < Integer.MAX_VALUE) {
+ while (i < MAX_USER_ID) {
if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
- break;
+ return i;
}
i++;
}
- return i;
}
+ throw new IllegalStateException("No user id available!");
}
private String packageToRestrictionsFileName(String packageName) {
@@ -2124,7 +2044,13 @@ public class UserManagerService extends IUserManager.Stub {
sb.append(" ago");
pw.println(sb);
}
+ pw.println(" Restrictions:");
+ UserRestrictionsUtils.dumpRestrictions(
+ pw, " ", mUserRestrictions.get(user.id));
}
+ pw.println();
+ pw.println("Guest restrictions:");
+ UserRestrictionsUtils.dumpRestrictions(pw, " ", mGuestRestrictions);
}
}
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
new file mode 100644
index 000000000000..db1fd2e28dfa
--- /dev/null
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import com.google.android.collect.Sets;
+
+import com.android.internal.util.Preconditions;
+
+import android.os.Bundle;
+import android.os.UserManager;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Set;
+
+public class UserRestrictionsUtils {
+ private UserRestrictionsUtils() {
+ }
+
+ public static final String[] USER_RESTRICTIONS = {
+ UserManager.DISALLOW_CONFIG_WIFI,
+ UserManager.DISALLOW_MODIFY_ACCOUNTS,
+ UserManager.DISALLOW_INSTALL_APPS,
+ UserManager.DISALLOW_UNINSTALL_APPS,
+ UserManager.DISALLOW_SHARE_LOCATION,
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+ UserManager.DISALLOW_CONFIG_BLUETOOTH,
+ UserManager.DISALLOW_USB_FILE_TRANSFER,
+ UserManager.DISALLOW_CONFIG_CREDENTIALS,
+ UserManager.DISALLOW_REMOVE_USER,
+ UserManager.DISALLOW_DEBUGGING_FEATURES,
+ UserManager.DISALLOW_CONFIG_VPN,
+ UserManager.DISALLOW_CONFIG_TETHERING,
+ UserManager.DISALLOW_NETWORK_RESET,
+ UserManager.DISALLOW_FACTORY_RESET,
+ UserManager.DISALLOW_ADD_USER,
+ UserManager.ENSURE_VERIFY_APPS,
+ UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
+ UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+ UserManager.DISALLOW_APPS_CONTROL,
+ UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
+ UserManager.DISALLOW_UNMUTE_MICROPHONE,
+ UserManager.DISALLOW_ADJUST_VOLUME,
+ UserManager.DISALLOW_OUTGOING_CALLS,
+ UserManager.DISALLOW_SMS,
+ UserManager.DISALLOW_FUN,
+ UserManager.DISALLOW_CREATE_WINDOWS,
+ UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
+ UserManager.DISALLOW_OUTGOING_BEAM,
+ UserManager.DISALLOW_WALLPAPER,
+ UserManager.DISALLOW_SAFE_BOOT,
+ UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
+ UserManager.DISALLOW_RECORD_AUDIO,
+ };
+
+ /**
+ * Set of user restrictions, which can only be enforced by the system.
+ */
+ public static final Set<String> SYSTEM_CONTROLLED_USER_RESTRICTIONS = Sets.newArraySet(
+ UserManager.DISALLOW_RECORD_AUDIO);
+
+ /**
+ * Set of user restriction which we don't want to persist.
+ */
+ public static final Set<String> NON_PERSIST_USER_RESTRICTIONS = Sets.newArraySet(
+ UserManager.DISALLOW_RECORD_AUDIO);
+
+ public static void writeRestrictions(XmlSerializer serializer, Bundle restrictions,
+ String tag) throws IOException {
+ serializer.startTag(null, tag);
+ for (String key : USER_RESTRICTIONS) {
+ //
+ if (restrictions.getBoolean(key)
+ && !NON_PERSIST_USER_RESTRICTIONS.contains(key)) {
+ serializer.attribute(null, key, "true");
+ }
+ }
+ serializer.endTag(null, tag);
+ }
+
+ public static void readRestrictions(XmlPullParser parser, Bundle restrictions)
+ throws IOException {
+ for (String key : USER_RESTRICTIONS) {
+ final String value = parser.getAttributeValue(null, key);
+ if (value != null) {
+ restrictions.putBoolean(key, Boolean.parseBoolean(value));
+ }
+ }
+ }
+
+ public static void dumpRestrictions(PrintWriter pw, String prefix, Bundle restrictions) {
+ boolean noneSet = true;
+ if (restrictions != null) {
+ for (String key : restrictions.keySet()) {
+ if (restrictions.getBoolean(key, false)) {
+ pw.println(prefix + key);
+ noneSet = false;
+ }
+ }
+ }
+ if (noneSet) {
+ pw.println(prefix + "none");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c265000f13f5..16add371cbbb 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -16,6 +16,16 @@
package com.android.server.policy;
+import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.view.WindowManager.LayoutParams.*;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
+import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
+
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerInternal.SleepToken;
@@ -40,7 +50,6 @@ import android.content.pm.ResolveInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -130,14 +139,6 @@ import java.io.PrintWriter;
import java.util.HashSet;
import java.util.List;
-import static android.view.WindowManager.LayoutParams.*;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
-import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
-
/**
* WindowManagerPolicy implementation for the Android phone UI. This
* introduces a new method suffix, Lp, for an internal lock of the
@@ -778,6 +779,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private final Runnable mUpdateRotationRunnable = new Runnable() {
@Override
public void run() {
+ // send interaction hint to improve redraw performance
+ mPowerManagerInternal.powerHint(PowerManagerInternal.POWER_HINT_INTERACTION, 0);
updateRotation(false);
}
};
@@ -4625,7 +4628,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mStatusBarController.setBarShowingLw(true)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
- } else if (topIsFullscreen) {
+ } else if (topIsFullscreen
+ && !mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)
+ && !mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
if (mStatusBarController.setBarShowingLw(false)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
@@ -6712,6 +6717,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
+ final boolean dockedStackVisible = mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
+ final boolean freeformStackVisible =
+ mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID);
+ final boolean forceShowSystemBars = dockedStackVisible || freeformStackVisible;
+ // TODO(multi-window): Update to force opaque independently for status bar and nav bar.
+ // This will require refactoring the code to have separate vis flag for each bar so it can
+ // be adjusted independently.
+ final boolean forceOpaqueSystemBars = forceShowSystemBars;
+
// apply translucent bar vis flags
WindowState transWin = isStatusBarKeyguard() && !mHideLockScreen
? mStatusBar
@@ -6734,7 +6748,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
vis = (vis & ~flags) | (oldVis & flags);
}
- if (!areTranslucentBarsAllowed() && transWin != mStatusBar) {
+ if ((!areTranslucentBarsAllowed() && transWin != mStatusBar)
+ || forceOpaqueSystemBars) {
vis &= ~(View.NAVIGATION_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSLUCENT
| View.SYSTEM_UI_TRANSPARENT);
}
@@ -6742,24 +6757,21 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// update status bar
boolean immersiveSticky =
(vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
- boolean hideStatusBarWM =
- mTopFullscreenOpaqueWindowState != null &&
- (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
+ final boolean hideStatusBarWM =
+ mTopFullscreenOpaqueWindowState != null
+ && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
& WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
- boolean hideStatusBarSysui =
+ final boolean hideStatusBarSysui =
(vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
- boolean hideNavBarSysui =
+ final boolean hideNavBarSysui =
(vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
- boolean transientStatusBarAllowed =
- mStatusBar != null && (
- hideStatusBarWM
- || (hideStatusBarSysui && immersiveSticky)
- || statusBarHasFocus);
+ final boolean transientStatusBarAllowed = mStatusBar != null
+ && (statusBarHasFocus || (!forceShowSystemBars
+ && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
- boolean transientNavBarAllowed =
- mNavigationBar != null &&
- hideNavBarSysui && immersiveSticky;
+ final boolean transientNavBarAllowed = mNavigationBar != null
+ && !forceShowSystemBars && hideNavBarSysui && immersiveSticky;
final long now = SystemClock.uptimeMillis();
final boolean pendingPanic = mPendingPanicGestureUptime != 0
@@ -6772,11 +6784,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mNavigationBarController.showTransient();
}
- boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
+ final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
&& !transientStatusBarAllowed && hideStatusBarSysui;
- boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
+ final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
&& !transientNavBarAllowed;
- if (denyTransientStatus || denyTransientNav) {
+ if (denyTransientStatus || denyTransientNav || forceShowSystemBars) {
// clear the clearable flags instead
clearClearableFlagsLw();
vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index a0a31c0d8a0c..ced04336dfac 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -397,6 +397,10 @@ public final class PowerManagerService extends SystemService
// Set this to false to disable.
private boolean mUserInactiveOverrideFromWindowManager;
+ // The next possible user activity timeout after being explicitly told the user is inactive.
+ // Set to -1 when not told the user is inactive since the last period spent dozing or asleep.
+ private long mOverriddenTimeout = -1;
+
// The user activity timeout override from the window manager
// to allow the current foreground activity to override the user activity timeout.
// Use -1 to disable.
@@ -1034,6 +1038,7 @@ public final class PowerManagerService extends SystemService
if (mUserInactiveOverrideFromWindowManager) {
mUserInactiveOverrideFromWindowManager = false;
+ mOverriddenTimeout = -1;
}
if (mWakefulness == WAKEFULNESS_ASLEEP
@@ -1251,12 +1256,28 @@ public final class PowerManagerService extends SystemService
}
}
+ /**
+ * Logs the time the device would have spent awake before user activity timeout,
+ * had the system not been told the user was inactive.
+ */
+ private void logSleepTimeoutRecapturedLocked() {
+ final long now = SystemClock.uptimeMillis();
+ final long savedWakeTimeMs = mOverriddenTimeout - now;
+ if (savedWakeTimeMs >= 0) {
+ EventLog.writeEvent(EventLogTags.POWER_SOFT_SLEEP_REQUESTED, savedWakeTimeMs);
+ mOverriddenTimeout = -1;
+ }
+ }
+
private void finishWakefulnessChangeIfNeededLocked() {
if (mWakefulnessChanging && mDisplayReady) {
if (mWakefulness == WAKEFULNESS_DOZING
&& (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {
return; // wait until dream has enabled dozing
}
+ if (mWakefulness == WAKEFULNESS_DOZING || mWakefulness == WAKEFULNESS_ASLEEP) {
+ logSleepTimeoutRecapturedLocked();
+ }
mWakefulnessChanging = false;
mNotifier.onWakefulnessChangeFinished();
}
@@ -1579,10 +1600,11 @@ public final class PowerManagerService extends SystemService
if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) {
if ((mUserActivitySummary &
(USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) {
- // Device is being kept awake by recent user activity
- long savedWakeTimeMs = now - nextTimeout;
- EventLog.writeEvent(
- EventLogTags.POWER_SOFT_SLEEP_REQUESTED, savedWakeTimeMs);
+ // Device is being kept awake by recent user activity
+ if (nextTimeout >= now && mOverriddenTimeout == -1) {
+ // Save when the next timeout would have occurred
+ mOverriddenTimeout = nextTimeout;
+ }
}
mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
nextTimeout = -1;
diff --git a/services/core/java/com/android/server/wm/DimBehindController.java b/services/core/java/com/android/server/wm/DimBehindController.java
new file mode 100644
index 000000000000..8870dd1ceef2
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DimBehindController.java
@@ -0,0 +1,299 @@
+package com.android.server.wm;
+
+import static com.android.server.wm.WindowManagerService.DEBUG_DIM_LAYER;
+
+import android.graphics.Rect;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.TypedValue;
+
+import java.io.PrintWriter;
+
+/**
+ * Centralizes the control of dim layers used for
+ * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}.
+ */
+class DimBehindController {
+ private static final String TAG = "DimBehindController";
+
+ /** Amount of time in milliseconds to animate the dim surface from one value to another,
+ * when no window animation is driving it. */
+ private static final int DEFAULT_DIM_DURATION = 200;
+
+ // Shared dim layer for fullscreen users. {@link DimBehindState#dimLayer} will point to this
+ // instead of creating a new object per fullscreen task on a display.
+ private DimLayer mSharedFullScreenDimLayer;
+
+ private ArrayMap<DimLayer.DimLayerUser, DimBehindState> mState = new ArrayMap<>();
+
+ private DisplayContent mDisplayContent;
+
+ private Rect mTmpBounds = new Rect();
+
+ DimBehindController(DisplayContent displayContent) {
+ mDisplayContent = displayContent;
+ }
+
+ /** Updates the dim layer bounds, recreating it if needed. */
+ void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
+ DimBehindState state = getOrCreateDimBehindState(dimLayerUser);
+ final boolean previousFullscreen = state.dimLayer != null
+ && state.dimLayer == mSharedFullScreenDimLayer;
+ DimLayer newDimLayer;
+ final int displayId = mDisplayContent.getDisplayId();
+ if (dimLayerUser.isFullscreen()) {
+ if (previousFullscreen) {
+ // Nothing to do here...
+ return;
+ }
+ // Use shared fullscreen dim layer
+ newDimLayer = mSharedFullScreenDimLayer;
+ if (newDimLayer == null) {
+ if (state.dimLayer != null) {
+ // Re-purpose the previous dim layer.
+ newDimLayer = state.dimLayer;
+ } else {
+ // Create new full screen dim layer.
+ newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId);
+ }
+ dimLayerUser.getBounds(mTmpBounds);
+ newDimLayer.setBounds(mTmpBounds);
+ mSharedFullScreenDimLayer = newDimLayer;
+ } else if (state.dimLayer != null) {
+ state.dimLayer. destroySurface();
+ }
+ } else {
+ newDimLayer = (state.dimLayer == null || previousFullscreen)
+ ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId)
+ : state.dimLayer;
+ dimLayerUser.getBounds(mTmpBounds);
+ newDimLayer.setBounds(mTmpBounds);
+ }
+ state.dimLayer = newDimLayer;
+ }
+
+ private DimBehindState getOrCreateDimBehindState(DimLayer.DimLayerUser dimLayerUser) {
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "getDimBehindState, dimLayerUser="
+ + dimLayerUser.toShortString());
+ DimBehindState state = mState.get(dimLayerUser);
+ if (state == null) {
+ state = new DimBehindState();
+ mState.put(dimLayerUser, state);
+ }
+ return state;
+ }
+
+ private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
+ DimBehindState state = mState.get(dimLayerUser);
+ if (state == null) {
+ if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: "
+ + dimLayerUser.toShortString());
+ return;
+ }
+ state.continueDimming = true;
+ }
+
+ boolean isDimming() {
+ for (int i = mState.size() - 1; i >= 0; i--) {
+ DimBehindState state = mState.valueAt(i);
+ if (state.dimLayer != null && state.dimLayer.isDimming()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void resetDimming() {
+ for (int i = mState.size() - 1; i >= 0; i--) {
+ mState.valueAt(i).continueDimming = false;
+ }
+ }
+
+ private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
+ DimBehindState state = mState.get(dimLayerUser);
+ return state != null && state.continueDimming;
+ }
+
+ void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
+ WindowStateAnimator newWinAnimator) {
+ // Only set dim params on the highest dimmed layer.
+ // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
+ DimBehindState state = getOrCreateDimBehindState(dimLayerUser);
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
+ + " dimLayerUser=" + dimLayerUser.toShortString()
+ + " newWinAnimator=" + newWinAnimator
+ + " state.animator=" + state.animator);
+ if (newWinAnimator.mSurfaceShown && (state.animator == null
+ || !state.animator.mSurfaceShown
+ || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) {
+ state.animator = newWinAnimator;
+ if (state.animator.mWin.mAppToken == null && !dimLayerUser.isFullscreen()) {
+ // Dim should cover the entire screen for system windows.
+ mDisplayContent.getLogicalDisplayRect(mTmpBounds);
+ state.dimLayer.setBounds(mTmpBounds);
+ }
+ }
+ }
+
+ void stopDimmingIfNeeded() {
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size());
+ for (int i = mState.size() - 1; i >= 0; i--) {
+ DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
+ stopDimmingIfNeeded(dimLayerUser);
+ }
+ }
+
+ private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
+ // No need to check if state is null, we know the key has a value.
+ DimBehindState state = mState.get(dimLayerUser);
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
+ + " dimLayerUser=" + dimLayerUser.toShortString()
+ + " state.continueDimming=" + state.continueDimming
+ + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
+ if (!state.continueDimming && state.dimLayer.isDimming()) {
+ state.animator = null;
+ dimLayerUser.getBounds(mTmpBounds);
+ state.dimLayer.setBounds(mTmpBounds);
+ }
+ }
+
+ boolean animateDimLayers() {
+ int fullScreen = -1;
+ int fullScreenAndDimming = -1;
+ boolean result = false;
+
+ for (int i = mState.size() - 1; i >= 0; i--) {
+ DimLayer.DimLayerUser user = mState.keyAt(i);
+ if (user.isFullscreen()) {
+ fullScreen = i;
+ if (mState.valueAt(i).continueDimming) {
+ fullScreenAndDimming = i;
+ }
+ } else {
+ // We always want to animate the non fullscreen windows, they don't share their
+ // dim layers.
+ result |= animateDimLayers(user);
+ }
+ }
+ // For the shared, full screen dim layer, we prefer the animation that is causing it to
+ // appear.
+ if (fullScreenAndDimming != -1) {
+ result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
+ } else if (fullScreen != -1) {
+ // If there is no animation for the full screen dim layer to appear, we can use any of
+ // the animators that will cause it to disappear.
+ result |= animateDimLayers(mState.keyAt(fullScreen));
+ }
+ return result;
+ }
+
+ private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
+ DimBehindState state = mState.get(dimLayerUser);
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
+ + " dimLayerUser=" + dimLayerUser.toShortString()
+ + " state.animator=" + state.animator
+ + " state.continueDimming=" + state.continueDimming);
+ final int dimLayer;
+ final float dimAmount;
+ if (state.animator == null) {
+ dimLayer = state.dimLayer.getLayer();
+ dimAmount = 0;
+ } else {
+ dimLayer = state.animator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
+ dimAmount = state.animator.mWin.mAttrs.dimAmount;
+ }
+ final float targetAlpha = state.dimLayer.getTargetAlpha();
+ if (targetAlpha != dimAmount) {
+ if (state.animator == null) {
+ state.dimLayer.hide(DEFAULT_DIM_DURATION);
+ } else {
+ long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
+ ? state.animator.mAnimation.computeDurationHint()
+ : DEFAULT_DIM_DURATION;
+ if (targetAlpha > dimAmount) {
+ duration = getDimBehindFadeDuration(duration);
+ }
+ state.dimLayer.show(dimLayer, dimAmount, duration);
+ }
+ } else if (state.dimLayer.getLayer() != dimLayer) {
+ state.dimLayer.setLayer(dimLayer);
+ }
+ if (state.dimLayer.isAnimating()) {
+ if (!mDisplayContent.mService.okToDisplay()) {
+ // Jump to the end of the animation.
+ state.dimLayer.show();
+ } else {
+ return state.dimLayer.stepAnimation();
+ }
+ }
+ return false;
+ }
+
+ boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
+ DimBehindState state = mState.get(dimLayerUser);
+ return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
+ }
+
+ private long getDimBehindFadeDuration(long duration) {
+ TypedValue tv = new TypedValue();
+ mDisplayContent.mService.mContext.getResources().getValue(
+ com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
+ if (tv.type == TypedValue.TYPE_FRACTION) {
+ duration = (long) tv.getFraction(duration, duration);
+ } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
+ duration = tv.data;
+ }
+ return duration;
+ }
+
+ void close() {
+ for (int i = mState.size() - 1; i >= 0; i--) {
+ DimBehindState state = mState.valueAt(i);
+ state.dimLayer.destroySurface();
+ }
+ mState.clear();
+ mSharedFullScreenDimLayer = null;
+ }
+
+ void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
+ mState.remove(dimLayerUser);
+ }
+
+ void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
+ if (dimLayerUser == null) {
+ Slog.e(TAG, "Trying to apply dim layer for: " + this
+ + ", but no dim layer user found.");
+ return;
+ }
+ if (!getContinueDimming(dimLayerUser)) {
+ setContinueDimming(dimLayerUser);
+ if (!isDimming(dimLayerUser, animator)) {
+ if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
+ startDimmingIfNeeded(dimLayerUser, animator);
+ }
+ }
+ }
+
+ private static class DimBehindState {
+ // The particular window with FLAG_DIM_BEHIND set. If null, hide dimLayer.
+ WindowStateAnimator animator;
+ // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
+ // end then stop any dimming.
+ boolean continueDimming;
+ DimLayer dimLayer;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "DimBehindController");
+ for (int i = 0, n = mState.size(); i < n; i++) {
+ pw.println(prefix + " " + mState.keyAt(i).toShortString());
+ pw.print(prefix + " ");
+ DimBehindState state = mState.valueAt(i);
+ pw.print("dimLayer=" + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" :
+ state.dimLayer));
+ pw.print(", animator=" + state.animator);
+ pw.println(", continueDimming=" + state.continueDimming + "}");
+
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index 538d6b846d46..bc3127434471 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -29,6 +29,10 @@ public class DimLayer {
private static final String TAG = "DimLayer";
private static final boolean DEBUG = false;
+ public static final float RESIZING_HINT_ALPHA = 0.5f;
+
+ public static final int RESIZING_HINT_DURATION_MS = 0;
+
/** Actual surface that dims */
SurfaceControl mDimSurface;
@@ -65,6 +69,9 @@ public class DimLayer {
boolean isFullscreen();
/** Returns the display info. of the dim layer user. */
DisplayInfo getDisplayInfo();
+ /** Gets the bounds of the dim layer user. */
+ void getBounds(Rect outBounds);
+ String toShortString();
}
/** The user of this dim layer. */
final DimLayerUser mUser;
@@ -239,8 +246,9 @@ public class DimLayer {
mDuration = duration;
}
}
- if (DEBUG) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime=" + mStartTime);
mTargetAlpha = alpha;
+ if (DEBUG) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime=" + mStartTime
+ + " mTargetAlpha=" + mTargetAlpha);
}
/** Immediate hide.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 745874cba3f5..39479c1a9681 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -115,6 +115,8 @@ class DisplayContent {
final DockedStackDividerController mDividerControllerLocked;
+ final DimBehindController mDimBehindController;
+
/**
* @param display May not be null.
* @param service You know.
@@ -128,6 +130,7 @@ class DisplayContent {
mService = service;
initializeDisplayBaseInfo();
mDividerControllerLocked = new DockedStackDividerController(service.mContext, this);
+ mDimBehindController = new DimBehindController(this);
}
int getDisplayId() {
@@ -188,6 +191,7 @@ class DisplayContent {
void updateDisplayInfo() {
mDisplay.getDisplayInfo(mDisplayInfo);
mDisplay.getMetrics(mDisplayMetrics);
+ mDividerControllerLocked.updateDisplayInfo();
for (int i = mStacks.size() - 1; i >= 0; --i) {
mStacks.get(i).updateDisplayInfo(null);
}
@@ -246,6 +250,7 @@ class DisplayContent {
}
void detachStack(TaskStack stack) {
+ mDimBehindController.removeDimLayerUser(stack);
mStacks.remove(stack);
}
@@ -262,9 +267,16 @@ class DisplayContent {
final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = tasks.get(taskNdx);
- task.getBounds(mTmpRect);
- if (mTmpRect.contains(x, y)) {
- return task.mTaskId;
+ // We need to use the visible frame on the window for any touch-related tests.
+ // Can't use the task's bounds because the original task bounds might be adjusted
+ // to fit the content frame. For example, the presence of the IME adjusting the
+ // windows frames when the app window is the IME target.
+ final WindowState win = task.getTopAppMainWindow();
+ if (win != null) {
+ win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
+ if (mTmpRect.contains(x, y)) {
+ return task.mTaskId;
+ }
}
}
}
@@ -294,7 +306,7 @@ class DisplayContent {
// might be adjusted to fit the content frame. (One example is when the
// task is put to top-left quadrant, the actual visible frame would not
// start at (0,0) after it's adjusted for the status bar.)
- WindowState win = task.getTopAppMainWindow();
+ final WindowState win = task.getTopAppMainWindow();
if (win != null) {
win.getVisibleBounds(mTmpRect, !BOUNDS_FOR_TOUCH);
mTmpRect.inset(-delta, -delta);
@@ -382,54 +394,23 @@ class DisplayContent {
}
boolean animateDimLayers() {
- boolean result = false;
- for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- final Task task = tasks.get(taskNdx);
- result |= task.animateDimLayers();
- if (task.isFullscreen()) {
- // No point in continuing as this task covers the entire screen.
- // Also, fullscreen tasks all share the same dim layer, so we don't want
- // processing of fullscreen task below this one affecting the dim layer state.
- return result;
- }
- }
- }
- return result;
+ return mDimBehindController.animateDimLayers();
}
void resetDimming() {
- for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- tasks.get(taskNdx).clearContinueDimming();
- }
- }
+ mDimBehindController.resetDimming();
}
boolean isDimming() {
- for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- if (tasks.get(taskNdx).isDimming()) {
- return true;
- }
- }
- }
- return false;
+ return mDimBehindController.isDimming();
}
void stopDimmingIfNeeded() {
- for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
- for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
- tasks.get(taskNdx).stopDimmingIfNeeded();
- }
- }
+ mDimBehindController.stopDimmingIfNeeded();
}
void close() {
+ mDimBehindController.close();
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
mStacks.get(stackNdx).close();
}
@@ -576,6 +557,7 @@ class DisplayContent {
}
}
pw.println();
+ mDimBehindController.dump(prefix + " ", pw);
}
@Override
@@ -584,12 +566,7 @@ class DisplayContent {
}
TaskStack getDockedStackLocked() {
- for (int i = mStacks.size() - 1; i >= 0; i--) {
- TaskStack stack = mStacks.get(i);
- if (stack.mStackId == DOCKED_STACK_ID && stack.isVisibleLocked()) {
- return stack;
- }
- }
- return null;
+ final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+ return (stack != null && stack.isVisibleLocked()) ? stack : null;
}
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 8c5d319439ba..3c2864853fab 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
@@ -24,17 +25,22 @@ import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
import static android.view.WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING;
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static com.android.server.wm.DimLayer.RESIZING_HINT_ALPHA;
+import static com.android.server.wm.DimLayer.RESIZING_HINT_DURATION_MS;
+import static com.android.server.wm.TaskPositioner.SIDE_MARGIN_DIP;
import static com.android.server.wm.TaskStack.DOCKED_BOTTOM;
-import static com.android.server.wm.TaskStack.DOCKED_INVALID;
import static com.android.server.wm.TaskStack.DOCKED_LEFT;
import static com.android.server.wm.TaskStack.DOCKED_RIGHT;
import static com.android.server.wm.TaskStack.DOCKED_TOP;
+import static com.android.server.wm.WindowManagerService.dipToPixel;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.RemoteException;
import android.util.Slog;
+import android.view.DisplayInfo;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -44,11 +50,15 @@ import android.view.WindowManagerGlobal;
/**
* Controls showing and hiding of a docked stack divider on the display.
*/
-public class DockedStackDividerController implements View.OnTouchListener {
+public class DockedStackDividerController implements View.OnTouchListener, DimLayer.DimLayerUser {
private static final String TAG = "DockedStackDivider";
private final Context mContext;
private final int mDividerWidth;
private final DisplayContent mDisplayContent;
+ private final int mSideMargin;
+ private final DimLayer mDimLayer;
+ private int mDisplayWidth;
+ private int mDisplayHeight;
private View mView;
private Rect mTmpRect = new Rect();
private Rect mLastResizeRect = new Rect();
@@ -57,22 +67,28 @@ public class DockedStackDividerController implements View.OnTouchListener {
private TaskStack mTaskStack;
private Rect mOriginalRect = new Rect();
private int mDockSide;
-
+ private boolean mDimLayerVisible;
DockedStackDividerController(Context context, DisplayContent displayContent) {
mContext = context;
mDisplayContent = displayContent;
+ updateDisplayInfo();
mDividerWidth = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
+ mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayContent.getDisplayMetrics());
+ mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId());
}
- private void addDivider() {
+ private void addDivider(Configuration configuration) {
View view = LayoutInflater.from(mContext).inflate(
com.android.internal.R.layout.docked_stack_divider, null);
view.setOnTouchListener(this);
WindowManagerGlobal manager = WindowManagerGlobal.getInstance();
+ final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
+ final int width = landscape ? mDividerWidth : MATCH_PARENT;
+ final int height = landscape ? MATCH_PARENT : mDividerWidth;
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- mDividerWidth, MATCH_PARENT, TYPE_DOCK_DIVIDER,
+ width, height, TYPE_DOCK_DIVIDER,
FLAG_TOUCHABLE_WHEN_WAKING | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
| FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH,
PixelFormat.OPAQUE);
@@ -92,10 +108,19 @@ public class DockedStackDividerController implements View.OnTouchListener {
return mView != null;
}
- void update() {
+ void updateDisplayInfo() {
+ final DisplayInfo info = mDisplayContent.getDisplayInfo();
+ mDisplayWidth = info.logicalWidth;
+ mDisplayHeight = info.logicalHeight;
+ }
+
+ void update(Configuration configuration, boolean forceUpdate) {
+ if (forceUpdate && mView != null) {
+ removeDivider();
+ }
TaskStack stack = mDisplayContent.getDockedStackLocked();
if (stack != null && mView == null) {
- addDivider();
+ addDivider(configuration);
} else if (stack == null && mView != null) {
removeDivider();
}
@@ -143,28 +168,183 @@ public class DockedStackDividerController implements View.OnTouchListener {
mStartY = (int) event.getRawY();
synchronized (mDisplayContent.mService.mWindowMap) {
mTaskStack = mDisplayContent.getDockedStackLocked();
- mTaskStack.getBounds(mOriginalRect);
- mDockSide = mTaskStack.getDockSide();
+ if (mTaskStack != null) {
+ mTaskStack.getBounds(mOriginalRect);
+ mDockSide = mTaskStack.getDockSide();
+ }
}
break;
case MotionEvent.ACTION_MOVE:
if (mTaskStack != null) {
- resizeStack(event);
+ final int x = (int) event.getRawX();
+ final int y = (int) event.getRawY();
+ resizeStack(x, y);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
- mTaskStack = null;
+ if (mTaskStack != null) {
+ final int x = (int) event.getRawX();
+ final int y = (int) event.getRawY();
+ // At most one of these will be executed, the other one will exit early.
+ maybeDismissTaskStack(x, y);
+ maybeMaximizeTaskStack(x, y);
+ mTaskStack = null;
+ }
+ setDimLayerVisible(false);
mDockSide = TaskStack.DOCKED_INVALID;
break;
}
return true;
}
- private void resizeStack(MotionEvent event) {
+ private void maybeMaximizeTaskStack(int x, int y) {
+ final int distance = distanceFromFullScreen(mDockSide, x, y);
+ if (distance == -1) {
+ Slog.wtf(TAG, "maybeMaximizeTaskStack: Unknown dock side=" + mDockSide);
+ return;
+ }
+ if (distance <= mSideMargin) {
+ try {
+ mDisplayContent.mService.mActivityManager.resizeStack(mTaskStack.mStackId, null);
+ } catch (RemoteException e) {
+ // This can't happen because we are in the same process.
+ }
+ }
+ }
+
+ private void maybeDismissTaskStack(int x, int y) {
+ final int distance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
+ if (distance == -1) {
+ Slog.wtf(TAG, "maybeDismissTaskStack: Unknown dock side=" + mDockSide);
+ return;
+ }
+ if (distance <= mSideMargin) {
+ try {
+ mDisplayContent.mService.mActivityManager.removeStack(mTaskStack.mStackId);
+ } catch (RemoteException e) {
+ // This can't happen because we are in the same process.
+ }
+ }
+ }
+
+ private void updateDimLayer(int x, int y) {
+ final int dismissDistance = distanceFromDockSide(mDockSide, mOriginalRect, x, y);
+ final int maximizeDistance = distanceFromFullScreen(mDockSide, x, y);
+ if (dismissDistance == -1 || maximizeDistance == -1) {
+ Slog.wtf(TAG, "updateDimLayer: Unknown dock side=" + mDockSide);
+ return;
+ }
+ if (dismissDistance <= mSideMargin && maximizeDistance <= mSideMargin) {
+ Slog.wtf(TAG, "Both dismiss and maximize distances would trigger dim layer.");
+ return;
+ }
+ if (dismissDistance <= mSideMargin) {
+ setDismissDimLayerVisible(x, y);
+ } else if (maximizeDistance <= mSideMargin) {
+ setMaximizeDimLayerVisible(x, y);
+ } else {
+ setDimLayerVisible(false);
+ }
+ }
+
+ /**
+ * Provides the distance from the point to the docked side of a rectangle.
+ *
+ * @return non negative distance or -1 on error
+ */
+ private static int distanceFromDockSide(int dockSide, Rect bounds, int x, int y) {
+ switch (dockSide) {
+ case DOCKED_LEFT:
+ return x - bounds.left;
+ case DOCKED_TOP:
+ return y - bounds.top;
+ case DOCKED_RIGHT:
+ return bounds.right - x;
+ case DOCKED_BOTTOM:
+ return bounds.bottom - y;
+ }
+ return -1;
+ }
+
+ private int distanceFromFullScreen(int dockSide, int x, int y) {
+ switch (dockSide) {
+ case DOCKED_LEFT:
+ return mDisplayWidth - x;
+ case DOCKED_TOP:
+ return mDisplayHeight - y;
+ case DOCKED_RIGHT:
+ return x;
+ case DOCKED_BOTTOM:
+ return y;
+ }
+ return -1;
+ }
+
+ private void setDismissDimLayerVisible(int x, int y) {
+ mTmpRect.set(mOriginalRect);
+ switch (mDockSide) {
+ case DOCKED_LEFT:
+ mTmpRect.right = x;
+ break;
+ case DOCKED_TOP:
+ mTmpRect.bottom = y;
+ break;
+ case DOCKED_RIGHT:
+ mTmpRect.left = x;
+ break;
+ case DOCKED_BOTTOM:
+ mTmpRect.top = y;
+ break;
+ default:
+ Slog.wtf(TAG, "setDismissDimLayerVisible: Unknown dock side when setting dim "
+ + "layer=" + mDockSide);
+ return;
+ }
+ mDimLayer.setBounds(mTmpRect);
+ setDimLayerVisible(true);
+ }
+
+ private void setMaximizeDimLayerVisible(int x, int y) {
+ mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+ switch (mDockSide) {
+ case DOCKED_LEFT:
+ mTmpRect.left = x;
+ break;
+ case DOCKED_TOP:
+ mTmpRect.top = y;
+ break;
+ case DOCKED_RIGHT:
+ mTmpRect.right = x;
+ break;
+ case DOCKED_BOTTOM:
+ mTmpRect.top = y;
+ break;
+ default:
+ Slog.wtf(TAG, "setMaximizeDimLayerVisible: Unknown dock side when setting dim "
+ + "layer=" + mDockSide);
+ }
+ mDimLayer.setBounds(mTmpRect);
+ setDimLayerVisible(true);
+ }
+
+ private void setDimLayerVisible(boolean visible) {
+ if (mDimLayerVisible == visible) {
+ return;
+ }
+ mDimLayerVisible = visible;
+ if (mDimLayerVisible) {
+ mDimLayer.show(mDisplayContent.mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
+ RESIZING_HINT_DURATION_MS);
+ } else {
+ mDimLayer.hide();
+ }
+ }
+
+ private void resizeStack(int x, int y) {
mTmpRect.set(mOriginalRect);
- final int deltaX = (int) event.getRawX() - mStartX;
- final int deltaY = (int) event.getRawY() - mStartY;
+ final int deltaX = x - mStartX;
+ final int deltaY = y - mStartY;
switch (mDockSide) {
case DOCKED_LEFT:
mTmpRect.right += deltaX;
@@ -186,7 +366,9 @@ public class DockedStackDividerController implements View.OnTouchListener {
try {
mDisplayContent.mService.mActivityManager.resizeStack(DOCKED_STACK_ID, mTmpRect);
} catch (RemoteException e) {
+ // This can't happen because we are in the same process.
}
+ updateDimLayer(x, y);
}
boolean isResizing() {
@@ -196,4 +378,24 @@ public class DockedStackDividerController implements View.OnTouchListener {
int getWidthAdjustment() {
return getWidth() / 2;
}
+
+ @Override
+ public boolean isFullscreen() {
+ return false;
+ }
+
+ @Override
+ public DisplayInfo getDisplayInfo() {
+ return mDisplayContent.getDisplayInfo();
+ }
+
+ @Override
+ public void getBounds(Rect outBounds) {
+ // This dim layer user doesn't need this.
+ }
+
+ @Override
+ public String toShortString() {
+ return TAG;
+ }
}
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 7dd716ee0d4b..283d49815049 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -18,8 +18,9 @@ package com.android.server.wm;
import java.io.PrintWriter;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowStateAnimator.SurfaceTrace;
-
+import static com.android.server.wm.WindowStateAnimator.WINDOW_FREEZE_LAYER;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
@@ -42,7 +43,15 @@ class ScreenRotationAnimation {
static final boolean TWO_PHASE_ANIMATION = false;
static final boolean USE_CUSTOM_BLACK_FRAME = false;
- static final int FREEZE_LAYER = WindowManagerService.TYPE_LAYER_MULTIPLIER * 200;
+ /*
+ * Layers for screen rotation animation. We put these layers above
+ * WINDOW_FREEZE_LAYER so that screen freeze will cover all windows.
+ */
+ static final int SCREEN_FREEZE_LAYER_BASE = WINDOW_FREEZE_LAYER + TYPE_LAYER_MULTIPLIER;
+ static final int SCREEN_FREEZE_LAYER_ENTER = SCREEN_FREEZE_LAYER_BASE;
+ static final int SCREEN_FREEZE_LAYER_SCREENSHOT = SCREEN_FREEZE_LAYER_BASE + 1;
+ static final int SCREEN_FREEZE_LAYER_EXIT = SCREEN_FREEZE_LAYER_BASE + 2;
+ static final int SCREEN_FREEZE_LAYER_CUSTOM = SCREEN_FREEZE_LAYER_BASE + 3;
final Context mContext;
final DisplayContent mDisplayContent;
@@ -265,7 +274,7 @@ class ScreenRotationAnimation {
SurfaceControl.screenshot(SurfaceControl.getBuiltInDisplay(
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN), sur);
mSurfaceControl.setLayerStack(display.getLayerStack());
- mSurfaceControl.setLayer(FREEZE_LAYER + 1);
+ mSurfaceControl.setLayer(SCREEN_FREEZE_LAYER_SCREENSHOT);
mSurfaceControl.setAlpha(0);
mSurfaceControl.show();
sur.destroy();
@@ -545,8 +554,8 @@ class ScreenRotationAnimation {
Rect outer = new Rect(-mOriginalWidth*1, -mOriginalHeight*1,
mOriginalWidth*2, mOriginalHeight*2);
Rect inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
- mCustomBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 3,
- layerStack, false);
+ mCustomBlackFrame = new BlackFrame(session, outer, inner,
+ SCREEN_FREEZE_LAYER_CUSTOM, layerStack, false);
mCustomBlackFrame.setMatrix(mFrameInitialMatrix);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
@@ -585,8 +594,8 @@ class ScreenRotationAnimation {
mOriginalWidth*2, mOriginalHeight*2);
inner = new Rect(0, 0, mOriginalWidth, mOriginalHeight);
}
- mExitingBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER + 2,
- layerStack, mForceDefaultOrientation);
+ mExitingBlackFrame = new BlackFrame(session, outer, inner,
+ SCREEN_FREEZE_LAYER_EXIT, layerStack, mForceDefaultOrientation);
mExitingBlackFrame.setMatrix(mFrameInitialMatrix);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
@@ -608,8 +617,8 @@ class ScreenRotationAnimation {
Rect outer = new Rect(-finalWidth*1, -finalHeight*1,
finalWidth*2, finalHeight*2);
Rect inner = new Rect(0, 0, finalWidth, finalHeight);
- mEnteringBlackFrame = new BlackFrame(session, outer, inner, FREEZE_LAYER,
- layerStack, false);
+ mEnteringBlackFrame = new BlackFrame(session, outer, inner,
+ SCREEN_FREEZE_LAYER_ENTER, layerStack, false);
} catch (OutOfResourcesException e) {
Slog.w(TAG, "Unable to allocate black surface", e);
} finally {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1f986dd90b10..c4600e02dfb9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -29,7 +29,6 @@ import android.graphics.Rect;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.TypedValue;
import android.view.DisplayInfo;
import android.view.Surface;
@@ -39,10 +38,6 @@ import java.io.PrintWriter;
import java.util.ArrayList;
class Task implements DimLayer.DimLayerUser {
- /** Amount of time in milliseconds to animate the dim surface from one value to another,
- * when no window animation is driving it. */
- private static final int DEFAULT_DIM_DURATION = 200;
-
// Return value from {@link setBounds} indicating no change was made to the Task bounds.
static final int BOUNDS_CHANGE_NONE = 0;
// Return value from {@link setBounds} indicating the position of the Task bounds changed.
@@ -78,17 +73,6 @@ class Task implements DimLayer.DimLayerUser {
// Whether the task is currently being drag-resized
private boolean mDragResizing;
- // The particular window with FLAG_DIM_BEHIND set. If null, hide mDimLayer.
- WindowStateAnimator mDimWinAnimator;
- // Used to support {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
- private DimLayer mDimLayer;
- // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the end
- // then stop any dimming.
- private boolean mContinueDimming;
- // Shared dim layer for fullscreen tasks. {@link #mDimLayer} will point to this instead
- // of creating a new object per fullscreen task on a display.
- private static final SparseArray<DimLayer> sSharedFullscreenDimLayers = new SparseArray<>();
-
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
Configuration config) {
mTaskId = taskId;
@@ -128,6 +112,10 @@ class Task implements DimLayer.DimLayerUser {
if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing taskId=" + mTaskId);
EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "removeTask");
mDeferRemoval = false;
+ DisplayContent content = getDisplayContent();
+ if (content != null) {
+ content.mDimBehindController.removeDimLayerUser(this);
+ }
mStack.removeTask(this);
mService.mTaskIdToTask.delete(mTaskId);
}
@@ -228,7 +216,9 @@ class Task implements DimLayer.DimLayerUser {
mBounds.set(bounds);
mRotation = rotation;
- updateDimLayer();
+ if (displayContent != null) {
+ displayContent.mDimBehindController.updateDimLayer(this);
+ }
mOverrideConfig = mFullscreen ? Configuration.EMPTY : config;
return boundsChange;
}
@@ -261,7 +251,8 @@ class Task implements DimLayer.DimLayerUser {
}
/** Bounds of the task with other system factors taken into consideration. */
- void getBounds(Rect out) {
+ @Override
+ public void getBounds(Rect out) {
if (useCurrentBounds()) {
// No need to adjust the output bounds if fullscreen or the docked stack is visible
// since it is already what we want to represent to the rest of the system.
@@ -303,142 +294,12 @@ class Task implements DimLayer.DimLayerUser {
if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) {
// Post message to inform activity manager of the bounds change simulating
// a one-way call. We do this to prevent a deadlock between window manager
- // lock and activity manager lock been held.
- mService.mH.sendMessage(mService.mH.obtainMessage(
- RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds));
- }
- }
-
- /** Updates the dim layer bounds, recreating it if needed. */
- private void updateDimLayer() {
- DimLayer newDimLayer;
- final boolean previousFullscreen =
- mDimLayer != null && sSharedFullscreenDimLayers.indexOfValue(mDimLayer) > -1;
- final int displayId = mStack.getDisplayContent().getDisplayId();
- if (mFullscreen) {
- if (previousFullscreen) {
- // Nothing to do here...
- return;
+ // lock and activity manager lock been held. Only tasks within the freeform stack
+ // are resizeable independently of their stack resizing.
+ if (mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ mService.mH.sendMessage(mService.mH.obtainMessage(
+ RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds));
}
- // Use shared fullscreen dim layer
- newDimLayer = sSharedFullscreenDimLayers.get(displayId);
- if (newDimLayer == null) {
- if (mDimLayer != null) {
- // Re-purpose the previous dim layer.
- newDimLayer = mDimLayer;
- } else {
- // Create new full screen dim layer.
- newDimLayer = new DimLayer(mService, this, displayId);
- }
- newDimLayer.setBounds(mBounds);
- sSharedFullscreenDimLayers.put(displayId, newDimLayer);
- } else if (mDimLayer != null) {
- mDimLayer.destroySurface();
- }
- } else {
- newDimLayer = (mDimLayer == null || previousFullscreen)
- ? new DimLayer(mService, this, displayId) : mDimLayer;
- newDimLayer.setBounds(mBounds);
- }
- mDimLayer = newDimLayer;
- }
-
- boolean animateDimLayers() {
- final int dimLayer;
- final float dimAmount;
- if (mDimWinAnimator == null) {
- dimLayer = mDimLayer.getLayer();
- dimAmount = 0;
- } else {
- dimLayer = mDimWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM;
- dimAmount = mDimWinAnimator.mWin.mAttrs.dimAmount;
- }
- final float targetAlpha = mDimLayer.getTargetAlpha();
- if (targetAlpha != dimAmount) {
- if (mDimWinAnimator == null) {
- mDimLayer.hide(DEFAULT_DIM_DURATION);
- } else {
- long duration = (mDimWinAnimator.mAnimating && mDimWinAnimator.mAnimation != null)
- ? mDimWinAnimator.mAnimation.computeDurationHint()
- : DEFAULT_DIM_DURATION;
- if (targetAlpha > dimAmount) {
- duration = getDimBehindFadeDuration(duration);
- }
- mDimLayer.show(dimLayer, dimAmount, duration);
- }
- } else if (mDimLayer.getLayer() != dimLayer) {
- mDimLayer.setLayer(dimLayer);
- }
- if (mDimLayer.isAnimating()) {
- if (!mService.okToDisplay()) {
- // Jump to the end of the animation.
- mDimLayer.show();
- } else {
- return mDimLayer.stepAnimation();
- }
- }
- return false;
- }
-
- private long getDimBehindFadeDuration(long duration) {
- TypedValue tv = new TypedValue();
- mService.mContext.getResources().getValue(
- com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
- if (tv.type == TypedValue.TYPE_FRACTION) {
- duration = (long)tv.getFraction(duration, duration);
- } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
- duration = tv.data;
- }
- return duration;
- }
-
- void clearContinueDimming() {
- mContinueDimming = false;
- }
-
- void setContinueDimming() {
- mContinueDimming = true;
- }
-
- boolean getContinueDimming() {
- return mContinueDimming;
- }
-
- boolean isDimming() {
- return mDimLayer.isDimming();
- }
-
- boolean isDimming(WindowStateAnimator winAnimator) {
- return mDimWinAnimator == winAnimator && isDimming();
- }
-
- void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
- // Only set dim params on the highest dimmed layer.
- // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
- if (newWinAnimator.mSurfaceShown && (mDimWinAnimator == null
- || !mDimWinAnimator.mSurfaceShown
- || mDimWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
- mDimWinAnimator = newWinAnimator;
- if (mDimWinAnimator.mWin.mAppToken == null
- && !mFullscreen && mStack.getDisplayContent() != null) {
- // Dim should cover the entire screen for system windows.
- mStack.getDisplayContent().getLogicalDisplayRect(mTmpRect);
- mDimLayer.setBounds(mTmpRect);
- }
- }
- }
-
- void stopDimmingIfNeeded() {
- if (!mContinueDimming && isDimming()) {
- mDimWinAnimator = null;
- mDimLayer.setBounds(mBounds);
- }
- }
-
- void close() {
- if (mDimLayer != null) {
- mDimLayer.destroySurface();
- mDimLayer = null;
}
}
@@ -491,16 +352,14 @@ class Task implements DimLayer.DimLayerUser {
return "{taskId=" + mTaskId + " appTokens=" + mAppTokens + " mdr=" + mDeferRemoval + "}";
}
+ @Override
+ public String toShortString() {
+ return "Task=" + mTaskId;
+ }
+
public void printTo(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("taskId="); pw.print(mTaskId);
pw.print(prefix); pw.print("appTokens="); pw.print(mAppTokens);
pw.print(prefix); pw.print("mdr="); pw.println(mDeferRemoval);
- if (mDimLayer.isDimming()) {
- pw.print(prefix); pw.println("mDimLayer:");
- mDimLayer.printTo(prefix + " ", pw);
- pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
- } else {
- pw.println();
- }
}
}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index df2e5e80b59b..227b3f01fac2 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -19,12 +19,15 @@ package com.android.server.wm;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.RESIZE_MODE_FORCED;
import static android.app.ActivityManager.RESIZE_MODE_USER;
+import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static com.android.server.wm.DimLayer.RESIZING_HINT_ALPHA;
+import static com.android.server.wm.DimLayer.RESIZING_HINT_DURATION_MS;
import static com.android.server.wm.WindowManagerService.DEBUG_TASK_POSITIONING;
import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerService.dipToPixel;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
@@ -60,7 +63,7 @@ class TaskPositioner implements DimLayer.DimLayerUser {
// The margin the pointer position has to be within the side of the screen to be
// considered at the side of the screen.
- private static final int SIDE_MARGIN_DIP = 100;
+ static final int SIDE_MARGIN_DIP = 100;
@IntDef(flag = true,
value = {
@@ -146,13 +149,18 @@ class TaskPositioner implements DimLayer.DimLayerUser {
}
synchronized (mService.mWindowMap) {
mDragEnded = notifyMoveLocked(newX, newY);
+ mTask.getBounds(mTmpRect);
+ }
+ if (!mTmpRect.equals(mWindowDragBounds)) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+ "wm.TaskPositioner.resizeTask");
+ try {
+ mService.mActivityManager.resizeTask(
+ mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
+ } catch (RemoteException e) {
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.TaskPositioner.resizeTask");
- try {
- mService.mActivityManager.resizeTask(
- mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
- } catch(RemoteException e) {}
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
} break;
case MotionEvent.ACTION_UP: {
@@ -171,15 +179,16 @@ class TaskPositioner implements DimLayer.DimLayerUser {
}
if (mDragEnded) {
+ final boolean wasResizing = mResizing;
synchronized (mService.mWindowMap) {
endDragLocked();
}
try {
- if (mResizing) {
+ if (wasResizing) {
// We were using fullscreen surface during resizing. Request
// resizeTask() one last time to restore surface to window size.
mService.mActivityManager.resizeTask(
- mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_FORCED);
+ mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
}
if (mCurrentDimSide != CTRL_NONE) {
@@ -240,7 +249,7 @@ class TaskPositioner implements DimLayer.DimLayerUser {
mDisplay.getDisplayId());
mDragWindowHandle.name = TAG;
mDragWindowHandle.inputChannel = mServerChannel;
- mDragWindowHandle.layer = getDragLayerLocked();
+ mDragWindowHandle.layer = mService.getDragLayerLocked();
mDragWindowHandle.layoutParamsFlags = 0;
mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
mDragWindowHandle.dispatchingTimeoutNanos =
@@ -273,9 +282,9 @@ class TaskPositioner implements DimLayer.DimLayerUser {
mService.pauseRotationLocked();
mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
- mSideMargin = mService.dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
- mMinVisibleWidth = mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
- mMinVisibleHeight = mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
+ mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
+ mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
+ mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
mDragEnded = false;
}
@@ -449,7 +458,8 @@ class TaskPositioner implements DimLayer.DimLayerUser {
}
mDimLayer.setBounds(mTmpRect);
- mDimLayer.show(getDragLayerLocked(), 0.5f, 0);
+ mDimLayer.show(mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
+ RESIZING_HINT_DURATION_MS);
}
@Override /** {@link DimLayer.DimLayerUser} */
@@ -462,9 +472,13 @@ class TaskPositioner implements DimLayer.DimLayerUser {
return mTask.mStack.getDisplayInfo();
}
- private int getDragLayerLocked() {
- return mService.mPolicy.windowTypeToLayerLw(WindowManager.LayoutParams.TYPE_DRAG)
- * WindowManagerService.TYPE_LAYER_MULTIPLIER
- + WindowManagerService.TYPE_LAYER_OFFSET;
+ @Override
+ public void getBounds(Rect out) {
+ // This dim layer user doesn't need this.
+ }
+
+ @Override
+ public String toShortString() {
+ return TAG;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 10ea4e2ba036..ca358b15bbb8 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -19,13 +19,13 @@ package com.android.server.wm;
import static android.app.ActivityManager.*;
import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT;
import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK;
+import static com.android.server.wm.WindowManagerService.H.UNUSED;
import static com.android.server.wm.WindowManagerService.TAG;
import android.annotation.IntDef;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Debug;
-import android.os.RemoteException;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -59,7 +59,7 @@ public class TaskStack implements DimLayer.DimLayerUser {
/** For comparison with DisplayContent bounds. */
private Rect mTmpRect = new Rect();
- private Rect TmpRect2 = new Rect();
+ private Rect mTmpRect2 = new Rect();
/** Content limits relative to the DisplayContent this sits in. */
private Rect mBounds = new Rect();
@@ -176,7 +176,11 @@ public class TaskStack implements DimLayer.DimLayerUser {
return false;
}
- mAnimationBackgroundSurface.setBounds(bounds);
+ if (mDisplayContent != null) {
+ mDisplayContent.mDimBehindController.updateDimLayer(this);
+ mAnimationBackgroundSurface.setBounds(bounds);
+ }
+
mBounds.set(bounds);
mRotation = rotation;
return true;
@@ -202,7 +206,8 @@ public class TaskStack implements DimLayer.DimLayerUser {
}
/** Bounds of the stack with other system factors taken into consideration. */
- void getBounds(Rect out) {
+ @Override
+ public void getBounds(Rect out) {
if (useCurrentBounds()) {
// No need to adjust the output bounds if fullscreen or the docked stack is visible
// since it is already what we want to represent to the rest of the system.
@@ -226,15 +231,15 @@ public class TaskStack implements DimLayer.DimLayerUser {
} else if (mFullscreen) {
setBounds(null);
} else {
- TmpRect2.set(mBounds);
+ mTmpRect2.set(mBounds);
mDisplayContent.rotateBounds(
- mRotation, mDisplayContent.getDisplayInfo().rotation, TmpRect2);
- if (setBounds(TmpRect2)) {
+ mRotation, mDisplayContent.getDisplayInfo().rotation, mTmpRect2);
+ if (setBounds(mTmpRect2)) {
// Post message to inform activity manager of the bounds change simulating
// a one-way call. We do this to prevent a deadlock between window manager
// lock and activity manager lock been held.
mService.mH.sendMessage(
- mService.mH.obtainMessage(RESIZE_STACK, mStackId, -1, mBounds));
+ mService.mH.obtainMessage(RESIZE_STACK, mStackId, UNUSED, mBounds));
}
}
}
@@ -383,14 +388,18 @@ public class TaskStack implements DimLayer.DimLayerUser {
mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId());
Rect bounds = null;
- final boolean dockedStackExists = mService.mStackIdToStack.get(DOCKED_STACK_ID) != null;
- if (mStackId == DOCKED_STACK_ID || (dockedStackExists
+ final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+ if (mStackId == DOCKED_STACK_ID || (dockedStack != null
&& mStackId >= FIRST_STATIC_STACK_ID && mStackId <= LAST_STATIC_STACK_ID)) {
// The existence of a docked stack affects the size of any static stack created since
// the docked stack occupies a dedicated region on screen.
bounds = new Rect();
displayContent.getLogicalDisplayRect(mTmpRect);
- getInitialDockedStackBounds(mTmpRect, bounds, mStackId,
+ mTmpRect2.setEmpty();
+ if (dockedStack != null) {
+ dockedStack.getRawBounds(mTmpRect2);
+ }
+ getInitialDockedStackBounds(mTmpRect, bounds, mStackId, mTmpRect2,
mDisplayContent.mDividerControllerLocked.getWidthAdjustment());
}
@@ -400,7 +409,7 @@ public class TaskStack implements DimLayer.DimLayerUser {
// Attaching a docked stack to the display affects the size of all other static
// stacks since the docked stack occupies a dedicated region on screen.
// Resize existing static stacks so they are pushed to the side of the docked stack.
- resizeNonDockedStacks(!FULLSCREEN);
+ resizeNonDockedStacks(!FULLSCREEN, mBounds);
}
}
@@ -410,29 +419,49 @@ public class TaskStack implements DimLayer.DimLayerUser {
* @param displayRect The bounds of the display the docked stack is on.
* @param outBounds Output bounds that should be used for the stack.
* @param stackId Id of stack we are calculating the bounds for.
+ * @param dockedBounds Bounds of the docked stack.
* @param adjustment
*/
- private static void getInitialDockedStackBounds(Rect displayRect, Rect outBounds, int stackId,
- int adjustment) {
- // Docked stack start off occupying half the screen space.
+ private static void getInitialDockedStackBounds(
+ Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int adjustment) {
final boolean dockedStack = stackId == DOCKED_STACK_ID;
final boolean splitHorizontally = displayRect.width() > displayRect.height();
final boolean topOrLeftCreateMode =
WindowManagerService.sDockedStackCreateMode == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
- final boolean placeTopOrLeft = (dockedStack && topOrLeftCreateMode)
- || (!dockedStack && !topOrLeftCreateMode);
+
outBounds.set(displayRect);
- if (placeTopOrLeft) {
+ if (dockedStack) {
+ // The initial bounds of the docked stack when it is created half the screen space and
+ // its bounds can be adjusted after that. The bounds of all other stacks are adjusted
+ // to occupy whatever screen space the docked stack isn't occupying.
+ if (topOrLeftCreateMode) {
+ if (splitHorizontally) {
+ outBounds.right = displayRect.centerX() - adjustment;
+ } else {
+ outBounds.bottom = displayRect.centerY() - adjustment;
+ }
+ } else {
+ if (splitHorizontally) {
+ outBounds.left = displayRect.centerX() + adjustment;
+ } else {
+ outBounds.top = displayRect.centerY() + adjustment;
+ }
+ }
+ return;
+ }
+
+ // Other stacks occupy whatever space is left by the docked stack.
+ if (!topOrLeftCreateMode) {
if (splitHorizontally) {
- outBounds.right = displayRect.centerX() - adjustment;
+ outBounds.right = dockedBounds.left - adjustment;
} else {
- outBounds.bottom = displayRect.centerY() - adjustment;
+ outBounds.bottom = dockedBounds.top - adjustment;
}
} else {
if (splitHorizontally) {
- outBounds.left = displayRect.centerX() + adjustment;
+ outBounds.left = dockedBounds.right + adjustment;
} else {
- outBounds.top = displayRect.centerY() + adjustment;
+ outBounds.top = dockedBounds.bottom + adjustment;
}
}
}
@@ -441,11 +470,14 @@ public class TaskStack implements DimLayer.DimLayerUser {
* based on the presence of a docked stack.
* @param fullscreen If true the stacks will be resized to fullscreen, else they will be
* resized to the appropriate size based on the presence of a docked stack.
+ * @param dockedBounds Bounds of the docked stack.
*/
- private void resizeNonDockedStacks(boolean fullscreen) {
- mDisplayContent.getLogicalDisplayRect(mTmpRect);
+ private void resizeNonDockedStacks(boolean fullscreen, Rect dockedBounds) {
+ // Not using mTmpRect because we are posting the object in a message.
+ final Rect bounds = new Rect();
+ mDisplayContent.getLogicalDisplayRect(bounds);
if (!fullscreen) {
- getInitialDockedStackBounds(mTmpRect, mTmpRect, FULLSCREEN_WORKSPACE_STACK_ID,
+ getInitialDockedStackBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds,
mDisplayContent.mDividerControllerLocked.getWidth());
}
@@ -456,11 +488,8 @@ public class TaskStack implements DimLayer.DimLayerUser {
if (otherStackId != DOCKED_STACK_ID
&& otherStackId >= FIRST_STATIC_STACK_ID
&& otherStackId <= LAST_STATIC_STACK_ID) {
- try {
- mService.mActivityManager.resizeStack(otherStackId, mTmpRect);
- } catch (RemoteException e) {
- // This will not happen since we are in the same process.
- }
+ mService.mH.sendMessage(
+ mService.mH.obtainMessage(RESIZE_STACK, otherStackId, UNUSED, bounds));
}
}
}
@@ -488,7 +517,7 @@ public class TaskStack implements DimLayer.DimLayerUser {
if (mStackId == DOCKED_STACK_ID) {
// Docked stack was detached from the display, so we no longer need to restrict the
// region of the screen other static stacks occupy. Go ahead and make them fullscreen.
- resizeNonDockedStacks(FULLSCREEN);
+ resizeNonDockedStacks(FULLSCREEN, null);
}
close();
@@ -527,9 +556,6 @@ public class TaskStack implements DimLayer.DimLayerUser {
mAnimationBackgroundSurface.destroySurface();
mAnimationBackgroundSurface = null;
}
- for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
- mTasks.get(taskNdx).close();
- }
mDisplayContent = null;
}
@@ -586,6 +612,11 @@ public class TaskStack implements DimLayer.DimLayerUser {
return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
}
+ @Override
+ public String toShortString() {
+ return "Stack=" + mStackId;
+ }
+
/**
* For docked workspace provides information which side of the screen was the dock anchored.
*/
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index ab1bf202f666..928a1170625c 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -272,25 +272,6 @@ public class WindowAnimator {
winAnimator.mWasAnimating = nowAnimating;
mAnimating |= nowAnimating;
- boolean appWindowAnimating = winAnimator.mAppAnimator != null
- && winAnimator.mAppAnimator.animating;
- boolean wasAppWindowAnimating = winAnimator.mAppAnimator != null
- && winAnimator.mAppAnimator.wasAnimating;
- boolean anyAnimating = appWindowAnimating || nowAnimating;
- boolean anyWasAnimating = wasAppWindowAnimating || wasAnimating;
-
- try {
- if (anyAnimating && !anyWasAnimating) {
- win.mClient.onAnimationStarted(winAnimator.mAnimatingMove ? -1
- : winAnimator.mKeyguardGoingAwayAnimation ? 1
- : 0);
- } else if (!anyAnimating && anyWasAnimating) {
- win.mClient.onAnimationStopped();
- }
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to dispatch window animation state change.", e);
- }
-
if (WindowManagerService.DEBUG_WALLPAPER) {
Slog.v(TAG, win + ": wasAnimating=" + wasAnimating +
", nowAnimating=" + nowAnimating);
@@ -753,6 +734,9 @@ public class WindowAnimator {
if (!mAnimating && wasAnimating) {
mWindowPlacerLocked.requestTraversal();
}
+
+ mService.destroyPreservedSurfaceLocked();
+
if (WindowManagerService.DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 74572cfcdee2..6239d2e4aafb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -18,6 +18,9 @@ package com.android.server.wm;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.DOCKED_STACK_ID;
+import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -216,13 +219,13 @@ public class WindowManagerService extends IWindowManager.Stub
static final boolean DEBUG_STACK = false;
static final boolean DEBUG_DISPLAY = false;
static final boolean DEBUG_POWER = false;
+ static final boolean DEBUG_DIM_LAYER = false;
static final boolean SHOW_SURFACE_ALLOC = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean SHOW_LIGHT_TRANSACTIONS = false || SHOW_TRANSACTIONS;
static final boolean HIDE_STACK_CRAWLS = true;
static final int LAYOUT_REPEAT_THRESHOLD = 4;
-
static final boolean PROFILE_ORIENTATION = false;
static final boolean localLOGV = DEBUG;
@@ -397,6 +400,13 @@ public class WindowManagerService extends IWindowManager.Stub
final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
/**
+ * Windows with a preserved surface waiting to be destroyed. These windows
+ * are going through a surface change. We keep the old surface around until
+ * the first frame on the new surface finishes drawing.
+ */
+ final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
+
+ /**
* Windows that have lost input focus and are waiting for the new
* focus window to be displayed before they are told about this.
*/
@@ -466,6 +476,11 @@ public class WindowManagerService extends IWindowManager.Stub
static int sDockedStackCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
+ int getDragLayerLocked() {
+ return mPolicy.windowTypeToLayerLw(LayoutParams.TYPE_DRAG) * TYPE_LAYER_MULTIPLIER
+ + TYPE_LAYER_OFFSET;
+ }
+
class RotationWatcher {
IRotationWatcher watcher;
IBinder.DeathRecipient deathRecipient;
@@ -1004,7 +1019,7 @@ public class WindowManagerService extends IWindowManager.Stub
* @param displayContent The display we are interested in.
* @return List of windows from token that are on displayContent.
*/
- WindowList getTokenWindowsOnDisplay(WindowToken token, DisplayContent displayContent) {
+ private WindowList getTokenWindowsOnDisplay(WindowToken token, DisplayContent displayContent) {
final WindowList windowList = new WindowList();
final int count = token.windows.size();
for (int i = 0; i < count; i++) {
@@ -1038,55 +1053,19 @@ public class WindowManagerService extends IWindowManager.Stub
}
private int addAppWindowToListLocked(final WindowState win) {
- final IWindow client = win.mClient;
- final WindowToken token = win.mToken;
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
// It doesn't matter this display is going away.
return 0;
}
+ final IWindow client = win.mClient;
+ final WindowToken token = win.mToken;
- final WindowList windows = win.getWindowList();
- final int N = windows.size();
+ final WindowList windows = displayContent.getWindowList();
WindowList tokenWindowList = getTokenWindowsOnDisplay(token, displayContent);
int tokenWindowsPos = 0;
- int windowListPos = tokenWindowList.size();
if (!tokenWindowList.isEmpty()) {
- // If this application has existing windows, we
- // simply place the new window on top of them... but
- // keep the starting window on top.
- if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
- // Base windows go behind everything else.
- WindowState lowestWindow = tokenWindowList.get(0);
- placeWindowBefore(lowestWindow, win);
- tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows);
- } else {
- AppWindowToken atoken = win.mAppToken;
- WindowState lastWindow = tokenWindowList.get(windowListPos - 1);
- if (atoken != null && lastWindow == atoken.startingWindow) {
- placeWindowBefore(lastWindow, win);
- tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows);
- } else {
- int newIdx = findIdxBasedOnAppTokens(win);
- //there is a window above this one associated with the same
- //apptoken note that the window could be a floating window
- //that was created later or a window at the top of the list of
- //windows associated with this token.
- if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
- "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of " +
- N);
- windows.add(newIdx + 1, win);
- if (newIdx < 0) {
- // No window from token found on win's display.
- tokenWindowsPos = 0;
- } else {
- tokenWindowsPos = indexOfWinInWindowList(
- windows.get(newIdx), token.windows) + 1;
- }
- mWindowsChanged = true;
- }
- }
- return tokenWindowsPos;
+ return addAppWindowToTokenListLocked(win, token, windows, tokenWindowList);
}
// No windows from this token on this display
@@ -1188,19 +1167,65 @@ public class WindowManagerService extends IWindowManager.Stub
// Just search for the start of this layer.
final int myLayer = win.mBaseLayer;
int i;
- for (i = N - 1; i >= 0; --i) {
+ for (i = windows.size() - 1; i >= 0; --i) {
WindowState w = windows.get(i);
- if (w.mBaseLayer <= myLayer) {
+ // Dock divider shares the base layer with application windows, but we want to always
+ // keep it above the application windows. The sharing of the base layer is intended
+ // for window animations, which need to be above the dock divider for the duration
+ // of the animation.
+ if (w.mBaseLayer <= myLayer && w.mAttrs.type != TYPE_DOCK_DIVIDER) {
break;
}
}
if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
- "Based on layer: Adding window " + win + " at " + (i + 1) + " of " + N);
+ "Based on layer: Adding window " + win + " at " + (i + 1) + " of "
+ + windows.size());
windows.add(i + 1, win);
mWindowsChanged = true;
return tokenWindowsPos;
}
+ private int addAppWindowToTokenListLocked(WindowState win, WindowToken token,
+ WindowList windows, WindowList tokenWindowList) {
+ int tokenWindowsPos;
+ // If this application has existing windows, we
+ // simply place the new window on top of them... but
+ // keep the starting window on top.
+ if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
+ // Base windows go behind everything else.
+ WindowState lowestWindow = tokenWindowList.get(0);
+ placeWindowBefore(lowestWindow, win);
+ tokenWindowsPos = indexOfWinInWindowList(lowestWindow, token.windows);
+ } else {
+ AppWindowToken atoken = win.mAppToken;
+ final int windowListPos = tokenWindowList.size();
+ WindowState lastWindow = tokenWindowList.get(windowListPos - 1);
+ if (atoken != null && lastWindow == atoken.startingWindow) {
+ placeWindowBefore(lastWindow, win);
+ tokenWindowsPos = indexOfWinInWindowList(lastWindow, token.windows);
+ } else {
+ int newIdx = findIdxBasedOnAppTokens(win);
+ //there is a window above this one associated with the same
+ //apptoken note that the window could be a floating window
+ //that was created later or a window at the top of the list of
+ //windows associated with this token.
+ if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+ "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of "
+ + windows.size());
+ windows.add(newIdx + 1, win);
+ if (newIdx < 0) {
+ // No window from token found on win's display.
+ tokenWindowsPos = 0;
+ } else {
+ tokenWindowsPos = indexOfWinInWindowList(
+ windows.get(newIdx), token.windows) + 1;
+ }
+ mWindowsChanged = true;
+ }
+ }
+ return tokenWindowsPos;
+ }
+
private void addFreeWindowToListLocked(final WindowState win) {
final WindowList windows = win.getWindowList();
@@ -1310,13 +1335,18 @@ public class WindowManagerService extends IWindowManager.Stub
static boolean canBeImeTarget(WindowState w) {
final int fl = w.mAttrs.flags
& (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM);
+ final int type = w.mAttrs.type;
+ // The dock divider has to sit above the application windows and so does the IME. IME also
+ // needs to sit above the dock divider, so it doesn't get cut in half. We make the dock
+ // divider be a target for IME, so this relationship can occur naturally.
if (fl == 0 || fl == (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)
- || w.mAttrs.type == TYPE_APPLICATION_STARTING) {
+ || type == TYPE_APPLICATION_STARTING || type == TYPE_DOCK_DIVIDER) {
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG, "isVisibleOrAdding " + w + ": " + w.isVisibleOrAdding());
if (!w.isVisibleOrAdding()) {
Slog.i(TAG, " mSurface=" + w.mWinAnimator.mSurfaceControl
- + " relayoutCalled=" + w.mRelayoutCalled + " viewVis=" + w.mViewVisibility
+ + " relayoutCalled=" + w.mRelayoutCalled
+ + " viewVis=" + w.mViewVisibility
+ " policyVis=" + w.mPolicyVisibility
+ " policyVisAfterAnim=" + w.mPolicyVisibilityAfterAnim
+ " attachHid=" + w.mAttachedHidden
@@ -2441,7 +2471,6 @@ public class WindowManagerService extends IWindowManager.Stub
boolean configChanged;
boolean surfaceChanged = false;
boolean dragResizing = false;
- boolean animating;
boolean hasStatusBarPermission =
mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
== PackageManager.PERMISSION_GRANTED;
@@ -2592,16 +2621,10 @@ public class WindowManagerService extends IWindowManager.Stub
// If we're starting a drag-resize, we'll be changing the surface size as well as
// notifying the client to render to with an offset from the surface's top-left.
- // Do a screen freeze, and keep the old surface until the the first frame drawn to
- // the new surface comes back, so that we avoid a flash due to mismatching surface
- // setups on window manager side and client side.
if (win.isDragResizeChanged()) {
win.setDragResizing();
if (win.mHasSurface) {
- winAnimator.mDestroyPendingSurfaceUponRedraw = true;
- winAnimator.mSurfaceDestroyDeferred = true;
- winAnimator.destroySurfaceLocked();
- startFreezingDisplayLocked(false, 0, 0);
+ winAnimator.preserveSurfaceLocked();
toBeDisplayed = true;
}
}
@@ -3179,9 +3202,9 @@ public class WindowManagerService extends IWindowManager.Stub
public int getOrientationLocked() {
if (mDisplayFrozen) {
- if (mLastWindowForcedOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Display is frozen, return "
- + mLastWindowForcedOrientation);
+ if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG,
+ "Display is frozen, return " + mLastWindowForcedOrientation);
// If the display is frozen, some activities may be in the middle
// of restarting, and thus have removed their old window. If the
// window has the flag to hide the lock screen, then the lock screen
@@ -3203,8 +3226,7 @@ public class WindowManagerService extends IWindowManager.Stub
continue;
}
int req = win.mAttrs.screenOrientation;
- if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
- (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
+ if(req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND) {
continue;
}
@@ -3214,7 +3236,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
return (mLastWindowForcedOrientation = req);
}
- mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
if (mPolicy.isKeyguardLocked()) {
// The screen is locked and no top system window is requesting an orientation.
@@ -3225,7 +3247,7 @@ public class WindowManagerService extends IWindowManager.Stub
null : winShowWhenLocked.mAppToken;
if (appShowWhenLocked != null) {
int req = appShowWhenLocked.requestedOrientation;
- if (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+ if (req == SCREEN_ORIENTATION_BEHIND) {
req = mLastKeyguardForcedOrientation;
}
if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + appShowWhenLocked
@@ -3238,8 +3260,21 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ final TaskStack dockedStack = mStackIdToStack.get(DOCKED_STACK_ID);
+ final TaskStack freeformStack = mStackIdToStack.get(FREEFORM_WORKSPACE_STACK_ID);
+ if ((dockedStack != null && dockedStack.isVisibleLocked())
+ || (freeformStack != null && freeformStack.isVisibleLocked())) {
+ // We don't let app affect the system orientation when in freeform or docked mode since
+ // they don't occupy the entire display and their request can conflict with other apps.
+ return SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
// Top system windows are not requesting an orientation. Start searching from apps.
- int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ return getAppSpecifiedOrientation();
+ }
+
+ private int getAppSpecifiedOrientation() {
+ int lastOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
boolean findingBehind = false;
boolean lastFullscreen = false;
// TODO: Multi window.
@@ -3256,19 +3291,16 @@ public class WindowManagerService extends IWindowManager.Stub
// if we're about to tear down this window and not seek for
// the behind activity, don't use it for orientation
if (!findingBehind && !atoken.hidden && atoken.hiddenRequested) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
- + " -- going to hide");
+ if (DEBUG_ORIENTATION) Slog.v(TAG,
+ "Skipping " + atoken + " -- going to hide");
continue;
}
if (tokenNdx == firstToken) {
- // If we have hit a new Task, and the bottom
- // of the previous group didn't explicitly say to use
- // the orientation behind it, and the last app was
- // full screen, then we'll stick with the
- // user's orientation.
- if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND
- && lastFullscreen) {
+ // If we have hit a new Task, and the bottom of the previous group didn't
+ // explicitly say to use the orientation behind it, and the last app was
+ // full screen, then we'll stick with the user's orientation.
+ if (lastOrientation != SCREEN_ORIENTATION_BEHIND && lastFullscreen) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
+ " -- end of group, return " + lastOrientation);
return lastOrientation;
@@ -3277,8 +3309,8 @@ public class WindowManagerService extends IWindowManager.Stub
// We ignore any hidden applications on the top.
if (atoken.hiddenRequested) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + atoken
- + " -- hidden on top");
+ if (DEBUG_ORIENTATION) Slog.v(TAG,
+ "Skipping " + atoken + " -- hidden on top");
continue;
}
@@ -3292,23 +3324,22 @@ public class WindowManagerService extends IWindowManager.Stub
// to use the orientation behind it, then just take whatever
// orientation it has and ignores whatever is under it.
lastFullscreen = atoken.appFullscreen;
- if (lastFullscreen && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
- + " -- full screen, return " + or);
+ if (lastFullscreen && or != SCREEN_ORIENTATION_BEHIND) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG,
+ "Done at " + atoken + " -- full screen, return " + or);
return or;
}
// If this application has requested an explicit orientation, then use it.
- if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
- && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
- if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + atoken
- + " -- explicitly set, return " + or);
+ if (or != SCREEN_ORIENTATION_UNSPECIFIED && or != SCREEN_ORIENTATION_BEHIND) {
+ if (DEBUG_ORIENTATION) Slog.v(TAG,
+ "Done at " + atoken + " -- explicitly set, return " + or);
return or;
}
- findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
+ findingBehind |= (or == SCREEN_ORIENTATION_BEHIND);
}
}
- if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation, return "
- + mForcedAppOrientation);
+ if (DEBUG_ORIENTATION) Slog.v(TAG,
+ "No app is requesting an orientation, return " + mForcedAppOrientation);
// The next app has not been requested to be visible, so we keep the current orientation
// to prevent freezing/unfreezing the display too early.
return mForcedAppOrientation;
@@ -3411,7 +3442,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- @Override
public void setNewConfiguration(Configuration config) {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"setNewConfiguration()")) {
@@ -3419,12 +3449,20 @@ public class WindowManagerService extends IWindowManager.Stub
}
synchronized(mWindowMap) {
+ final boolean orientationChanged = mCurConfiguration.orientation != config.orientation;
mCurConfiguration = new Configuration(config);
if (mWaitingForConfig) {
mWaitingForConfig = false;
mLastFinishedFreezeSource = "new-config";
}
mWindowPlacerLocked.performSurfacePlacement();
+ if (orientationChanged) {
+ for (int i = mDisplayContents.size() - 1; i >= 0; i--) {
+ DisplayContent content = mDisplayContents.valueAt(i);
+ Message.obtain(mH, H.UPDATE_DOCKED_STACK_DIVIDER, H.DOCK_DIVIDER_FORCE_UPDATE,
+ H.UNUSED, content).sendToTarget();
+ }
+ }
}
}
@@ -5558,10 +5596,13 @@ public class WindowManagerService extends IWindowManager.Stub
int retryCount = 0;
WindowState appWin = null;
- final boolean appIsImTarget = mInputMethodTarget != null
- && mInputMethodTarget.mAppToken != null
- && mInputMethodTarget.mAppToken.appToken != null
- && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+ boolean appIsImTarget;
+ synchronized(mWindowMap) {
+ appIsImTarget = mInputMethodTarget != null
+ && mInputMethodTarget.mAppToken != null
+ && mInputMethodTarget.mAppToken.appToken != null
+ && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+ }
final int aboveAppLayer = (mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
@@ -7164,6 +7205,21 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int RESIZE_STACK = 43;
public static final int RESIZE_TASK = 44;
+ /**
+ * Used to indicate in the message that the dock divider needs to be updated only if it's
+ * necessary.
+ */
+ static final int DOCK_DIVIDER_NO_FORCE_UPDATE = 0;
+ /**
+ * Used to indicate in the message that the dock divider should be force-removed before
+ * updating, so new configuration can be applied.
+ */
+ static final int DOCK_DIVIDER_FORCE_UPDATE = 1;
+ /**
+ * Used to denote that an integer field in a message will not be used.
+ */
+ public static final int UNUSED = 0;
+
@Override
public void handleMessage(Message msg) {
if (DEBUG_WINDOW_TRACE) {
@@ -7705,8 +7761,9 @@ public class WindowManagerService extends IWindowManager.Stub
break;
case UPDATE_DOCKED_STACK_DIVIDER: {
DisplayContent content = (DisplayContent) msg.obj;
+ final boolean forceUpdate = msg.arg1 == DOCK_DIVIDER_FORCE_UPDATE;
synchronized (mWindowMap) {
- content.mDividerControllerLocked.update();
+ content.mDividerControllerLocked.update(mCurConfiguration, forceUpdate);
}
}
break;
@@ -7733,6 +7790,13 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ void destroyPreservedSurfaceLocked() {
+ for (int i = mDestroyPreservedSurface.size() - 1; i >= 0 ; i--) {
+ final WindowState w = mDestroyPreservedSurface.get(i);
+ w.mWinAnimator.destroyPreservedSurfaceLocked();
+ }
+ mDestroyPreservedSurface.clear();
+ }
// -------------------------------------------------------------
// IWindowManager API
// -------------------------------------------------------------
@@ -8356,8 +8420,10 @@ public class WindowManagerService extends IWindowManager.Stub
layerChanged = true;
anyLayerChanged = true;
}
- final Task task = w.getTask();
- if (layerChanged && task != null && task.isDimming(winAnimator)) {
+ final DimLayer.DimLayerUser dimLayerUser = w.getDimLayerUser();
+ final DisplayContent displayContent = w.getDisplayContent();
+ if (layerChanged && dimLayerUser != null && displayContent != null &&
+ displayContent.mDimBehindController.isDimming(dimLayerUser, winAnimator)) {
// Force an animation pass just to update the mDimLayer layer.
scheduleAnimationLocked();
}
@@ -9114,7 +9180,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized (mWindowMap) {
int visibility = mPolicy.adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
updateStatusBarVisibilityLocked(visibility);
- mWindowPlacerLocked.performSurfacePlacement();
+ mWindowPlacerLocked.requestTraversal();
}
}
@@ -10082,5 +10148,12 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ @Override
+ public boolean isStackVisible(int stackId) {
+ synchronized (mWindowMap) {
+ final TaskStack stack = mStackIdToStack.get(stackId);
+ return (stack != null && stack.isVisibleLocked());
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 55ddbc06d959..383ad8cb4b07 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -29,6 +29,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static com.android.server.wm.WindowManagerService.DEBUG_CONFIGURATION;
+import static com.android.server.wm.WindowManagerService.DEBUG_DIM_LAYER;
import static com.android.server.wm.WindowManagerService.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerService.DEBUG_POWER;
@@ -582,12 +583,6 @@ final class WindowState implements WindowManagerPolicy.WindowState {
if (mContainingFrame.isEmpty()) {
mContainingFrame.set(cf);
}
- } else {
- // Make sure the containing frame is within the content frame so we don't layout
- // resized window under screen decorations.
- if (!mContainingFrame.intersect(cf)) {
- mContainingFrame.set(cf);
- }
}
mDisplayFrame.set(mContainingFrame);
} else {
@@ -698,10 +693,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
final int height = Math.min(mFrame.height(), mContentFrame.height());
final int width = Math.min(mContentFrame.width(), mFrame.width());
final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
- final int minVisibleHeight =
- mService.dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
- final int minVisibleWidth =
- mService.dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
+ final int minVisibleHeight = WindowManagerService.dipToPixel(
+ MINIMUM_VISIBLE_HEIGHT_IN_DP, displayMetrics);
+ final int minVisibleWidth = WindowManagerService.dipToPixel(
+ MINIMUM_VISIBLE_WIDTH_IN_DP, displayMetrics);
final int top = Math.max(mContentFrame.top,
Math.min(mFrame.top, mContentFrame.bottom - minVisibleHeight));
final int left = Math.max(mContentFrame.left + minVisibleWidth - width,
@@ -712,6 +707,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
mStableFrame.set(mContentFrame);
} else if (mAttrs.type == TYPE_DOCK_DIVIDER) {
mDisplayContent.mDividerControllerLocked.positionDockedStackedDivider(mFrame);
+ mContentFrame.set(mFrame);
} else {
mContentFrame.set(Math.max(mContentFrame.left, mFrame.left),
Math.max(mContentFrame.top, mFrame.top),
@@ -889,7 +885,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
@Override
public boolean isVoiceInteraction() {
- return mAppToken != null ? mAppToken.voiceInteraction : false;
+ return mAppToken != null && mAppToken.voiceInteraction;
}
boolean setInsetsChanged() {
@@ -934,7 +930,10 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return task.mStack;
}
}
- return null;
+ // Some system windows (e.g. "Power off" dialog) don't have a task, but we would still
+ // associate them with some stack to enable dimming.
+ return mAttrs.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW
+ && mDisplayContent != null ? mDisplayContent.getHomeStack() : null;
}
/**
@@ -970,7 +969,8 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
if (forTouch && inFreeformWorkspace()) {
final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
- final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
+ final int delta = WindowManagerService.dipToPixel(
+ RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
bounds.inset(-delta, -delta);
}
}
@@ -1031,8 +1031,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
return false;
}
final AppWindowToken atoken = mAppToken;
- final boolean animating = atoken != null
- ? (atoken.mAppAnimator.animation != null) : false;
+ final boolean animating = atoken != null && atoken.mAppAnimator.animation != null;
return mHasSurface && !mDestroying && !mExiting
&& (atoken == null ? mPolicyVisibility : !atoken.hiddenRequested)
&& ((!mAttachedHidden && mViewVisibility == View.VISIBLE
@@ -1114,8 +1113,7 @@ final class WindowState implements WindowManagerPolicy.WindowState {
* of a transition that has not yet been started.
*/
boolean isReadyForDisplay() {
- if (mRootToken.waitingToShow &&
- mService.mAppTransition.isTransitionSet()) {
+ if (mRootToken.waitingToShow && mService.mAppTransition.isTransitionSet()) {
return false;
}
return mHasSurface && mPolicyVisibility && !mDestroying
@@ -1295,19 +1293,20 @@ final class WindowState implements WindowManagerPolicy.WindowState {
}
void handleFlagDimBehind() {
- if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 && isDisplayedLw() && !mExiting) {
- final Task task = getTask();
- if (task == null) {
- return;
- }
- task.setContinueDimming();
- if (!task.isDimming(mWinAnimator)) {
- if (WindowManagerService.localLOGV) Slog.v(TAG, "Win " + this + " start dimming.");
- task.startDimmingIfNeeded(mWinAnimator);
- }
+ if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 && mDisplayContent != null && !mExiting
+ && isDisplayedLw()) {
+ mDisplayContent.mDimBehindController.applyDimBehind(getDimLayerUser(), mWinAnimator);
}
}
+ DimLayer.DimLayerUser getDimLayerUser() {
+ Task task = getTask();
+ if (task != null) {
+ return task;
+ }
+ return getStack();
+ }
+
void maybeRemoveReplacedWindow() {
AppWindowToken token = mAppToken;
if (token != null && token.mReplacingWindow) {
@@ -1512,11 +1511,9 @@ final class WindowState implements WindowManagerPolicy.WindowState {
@Override
public boolean isDimming() {
- Task task = getTask();
- if (task == null) {
- return false;
- }
- return task.isDimming(mWinAnimator);
+ final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
+ return dimLayerUser != null && mDisplayContent != null &&
+ mDisplayContent.mDimBehindController.isDimming(dimLayerUser, mWinAnimator);
}
public void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 60bf57154bc6..27339333b53e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -23,11 +23,12 @@ import static com.android.server.wm.WindowManagerService.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerService.DEBUG_ORIENTATION;
import static com.android.server.wm.WindowManagerService.DEBUG_STARTING_WINDOW;
import static com.android.server.wm.WindowManagerService.DEBUG_SURFACE_TRACE;
-import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerService.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerService.localLOGV;
import static com.android.server.wm.WindowManagerService.SHOW_LIGHT_TRANSACTIONS;
import static com.android.server.wm.WindowManagerService.SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.WindowManagerService.localLOGV;
+import static com.android.server.wm.WindowManagerService.SHOW_TRANSACTIONS;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
import static com.android.server.wm.WindowSurfacePlacer.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowSurfacePlacer.SET_TURN_ON_SCREEN;
@@ -66,6 +67,7 @@ import java.util.ArrayList;
**/
class WindowStateAnimator {
static final String TAG = "WindowStateAnimator";
+ static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200;
// Unchanging local convenience fields.
final WindowManagerService mService;
@@ -108,7 +110,7 @@ class WindowStateAnimator {
*/
boolean mSurfaceDestroyDeferred;
- boolean mDestroyPendingSurfaceUponRedraw;
+ boolean mDestroyPreservedSurfaceUponRedraw;
float mShownAlpha = 0;
float mAlpha = 0;
float mLastAlpha = 0;
@@ -290,6 +292,9 @@ class WindowStateAnimator {
// This must be called while inside a transaction. Returns true if
// there is more animation to run.
boolean stepAnimationLocked(long currentTime) {
+ // Save the animation state as it was before this step so WindowManagerService can tell if
+ // we just started or just stopped animating by comparing mWasAnimating with isAnimating().
+ mWasAnimating = mAnimating;
final DisplayContent displayContent = mWin.getDisplayContent();
if (displayContent != null && mService.okToDisplay()) {
// We will run animations as long as the display isn't frozen.
@@ -553,12 +558,10 @@ class WindowStateAnimator {
if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = performShowLocked();
}
- if (mDestroyPendingSurfaceUponRedraw) {
- mDestroyPendingSurfaceUponRedraw = false;
- destroyDeferredSurfaceLocked();
- mService.stopFreezingDisplayLocked();
+ if (mDestroyPreservedSurfaceUponRedraw && result) {
+ mService.mDestroyPreservedSurface.add(mWin);
}
- return false;
+ return result;
}
static class SurfaceTrace extends SurfaceControl {
@@ -778,6 +781,31 @@ class WindowStateAnimator {
}
}
+ void preserveSurfaceLocked() {
+ if (mDestroyPreservedSurfaceUponRedraw) {
+ return;
+ }
+ if (mSurfaceControl != null) {
+ SurfaceControl.openTransaction();
+ try {
+ mSurfaceControl.setLayer(WINDOW_FREEZE_LAYER);
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+ }
+ mDestroyPreservedSurfaceUponRedraw = true;
+ mSurfaceDestroyDeferred = true;
+ destroySurfaceLocked();
+ }
+
+ void destroyPreservedSurfaceLocked() {
+ if (!mDestroyPreservedSurfaceUponRedraw) {
+ return;
+ }
+ destroyDeferredSurfaceLocked();
+ mDestroyPreservedSurfaceUponRedraw = false;
+ }
+
SurfaceControl createSurfaceLocked() {
final WindowState w = mWin;
if (mSurfaceControl == null) {
@@ -1516,12 +1544,7 @@ class WindowStateAnimator {
mDsDy * w.mHScale, mDtDy * w.mVScale);
mAnimator.setPendingLayoutChanges(w.getDisplayId(),
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
- if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
- final Task task = w.getTask();
- if (task != null) {
- task.startDimmingIfNeeded(this);
- }
- }
+ w.handleFlagDimBehind();
} catch (RuntimeException e) {
// If something goes wrong with the surface (such
// as running out of memory), don't take down the
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index df0a1c9d43d5..112646ad7818 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -642,9 +642,7 @@ class WindowSurfacePlacer {
handleNotObscuredLocked(w, innerDw, innerDh);
}
- if (task != null && !task.getContinueDimming()) {
- w.handleFlagDimBehind();
- }
+ w.handleFlagDimBehind();
if (isDefaultDisplay && obscuredChanged
&& mWallpaperControllerLocked.isWallpaperTarget(w) && w.isVisibleLw()) {
@@ -967,7 +965,8 @@ class WindowSurfacePlacer {
}
mService.mPolicy.finishLayoutLw();
- mService.mH.obtainMessage(UPDATE_DOCKED_STACK_DIVIDER, displayContent).sendToTarget();
+ mService.mH.obtainMessage(UPDATE_DOCKED_STACK_DIVIDER,
+ DOCK_DIVIDER_NO_FORCE_UPDATE, UNUSED, displayContent).sendToTarget();
}
/**
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2dd7cdea7305..0860f025c888 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -124,6 +124,7 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.ActiveAdmin.TrustAgentInfo;
+import com.android.server.pm.UserRestrictionsUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -277,7 +278,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final LocalService mLocalService;
// Stores and loads state on device and profile owners.
- private final Owners mOwners;
+ @VisibleForTesting
+ final Owners mOwners;
private final Binder mToken = new Binder();
@@ -433,6 +435,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private static final String TAG_PROVIDER = "provider";
private static final String TAG_PACKAGE_LIST_ITEM = "item";
+ private static final String TAG_USER_RESTRICTIONS = "user-restrictions";
+
final DeviceAdminInfo info;
int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
@@ -512,6 +516,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
List<String> crossProfileWidgetProviders;
+ Bundle userRestrictions;
+
ActiveAdmin(DeviceAdminInfo _info) {
info = _info;
}
@@ -686,6 +692,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
writePackageListToXml(out, TAG_PERMITTED_ACCESSIBILITY_SERVICES,
permittedAccessiblityServices);
writePackageListToXml(out, TAG_PERMITTED_IMES, permittedInputMethods);
+ if (hasUserRestrictions()) {
+ UserRestrictionsUtils.writeRestrictions(
+ out, userRestrictions, TAG_USER_RESTRICTIONS);
+ }
}
void writePackageListToXml(XmlSerializer out, String outerTag,
@@ -795,6 +805,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
permittedAccessiblityServices = readPackageList(parser, tag);
} else if (TAG_PERMITTED_IMES.equals(tag)) {
permittedInputMethods = readPackageList(parser, tag);
+ } else if (TAG_USER_RESTRICTIONS.equals(tag)) {
+ UserRestrictionsUtils.readRestrictions(parser, ensureUserRestrictions());
} else {
Slog.w(LOG_TAG, "Unknown admin tag: " + tag);
XmlUtils.skipCurrentTag(parser);
@@ -915,6 +927,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return result;
}
+ boolean hasUserRestrictions() {
+ return userRestrictions != null && userRestrictions.size() > 0;
+ }
+
+ Bundle ensureUserRestrictions() {
+ if (userRestrictions == null) {
+ userRestrictions = new Bundle();
+ }
+ return userRestrictions;
+ }
+
void dump(String prefix, PrintWriter pw) {
pw.print(prefix); pw.print("uid="); pw.println(getUid());
pw.print(prefix); pw.println("policies:");
@@ -984,6 +1007,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
pw.print(prefix); pw.print("permittedInputMethods=");
pw.println(permittedInputMethods.toString());
}
+ pw.print(prefix); pw.println("userRestrictions:");
+ UserRestrictionsUtils.dumpRestrictions(pw, prefix + " ", userRestrictions);
}
}
@@ -1116,6 +1141,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return getCallingUid() == Process.myUid();
}
+ final int userHandleGetCallingUserId() {
+ return UserHandle.getUserId(binderGetCallingUid());
+ }
+
File environmentGetUserSystemDirectory(int userId) {
return Environment.getUserSystemDirectory(userId);
}
@@ -1151,6 +1180,42 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
String getDevicePolicyFilePathForSystemUser() {
return "/data/system/";
}
+
+ int settingsSecureGetIntForUser(String name, int def, int userHandle) {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ name, def, userHandle);
+ }
+
+ void settingsSecurePutIntForUser(String name, int value, int userHandle) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ name, value, userHandle);
+ }
+
+ void settingsSecurePutStringForUser(String name, String value, int userHandle) {
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ name, value, userHandle);
+ }
+
+ void settingsGlobalPutStringForUser(String name, String value, int userHandle) {
+ Settings.Global.putStringForUser(mContext.getContentResolver(),
+ name, value, userHandle);
+ }
+
+ void settingsSecurePutInt(String name, int value) {
+ Settings.Secure.putInt(mContext.getContentResolver(), name, value);
+ }
+
+ void settingsGlobalPutInt(String name, int value) {
+ Settings.Global.putInt(mContext.getContentResolver(), name, value);
+ }
+
+ void settingsSecurePutString(String name, String value) {
+ Settings.Secure.putString(mContext.getContentResolver(), name, value);
+ }
+
+ void settingsGlobalPutString(String name, String value) {
+ Settings.Global.putString(mContext.getContentResolver(), name, value);
+ }
}
/**
@@ -1261,10 +1326,59 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
void loadOwners() {
synchronized (this) {
mOwners.load();
+ findOwnerComponentIfNecessaryLocked();
+
+ // TODO PO may not have a class name either due to b/17652534. Address that too.
+
updateDeviceOwnerLocked();
}
}
+ private void findOwnerComponentIfNecessaryLocked() {
+ if (!mOwners.hasDeviceOwner()) {
+ return;
+ }
+ final ComponentName doComponentName = mOwners.getDeviceOwnerComponent();
+
+ if (!TextUtils.isEmpty(doComponentName.getClassName())) {
+ return; // Already a full component name.
+ }
+
+ final ComponentName doComponent = findAdminComponentWithPackageLocked(
+ doComponentName.getPackageName(),
+ mOwners.getDeviceOwnerUserId());
+ if (doComponent == null) {
+ Slog.e(LOG_TAG, "Device-owner isn't registered as device-admin");
+ } else {
+ mOwners.setDeviceOwner(
+ doComponent,
+ mOwners.getDeviceOwnerName(),
+ mOwners.getDeviceOwnerUserId());
+ mOwners.writeDeviceOwner();
+ }
+ }
+
+ private ComponentName findAdminComponentWithPackageLocked(String packageName, int userId) {
+ final DevicePolicyData policy = getUserData(userId);
+ final int n = policy.mAdminList.size();
+ ComponentName found = null;
+ int nFound = 0;
+ for (int i = 0; i < n; i++) {
+ final ActiveAdmin admin = policy.mAdminList.get(i);
+ if (packageName.equals(admin.info.getPackageName())) {
+ // Found!
+ if (nFound == 0) {
+ found = admin.info.getComponent();
+ }
+ nFound++;
+ }
+ }
+ if (nFound > 0) {
+ Slog.w(LOG_TAG, "Multiple DA found; assume the first one is DO.");
+ }
+ return found;
+ }
+
/**
* Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration
* reminders. Clears alarm if no expirations are configured.
@@ -1375,23 +1489,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return null;
}
- private boolean isActiveAdminWithPolicyForUserLocked(ActiveAdmin admin, int reqPolicy,
+ @VisibleForTesting
+ boolean isActiveAdminWithPolicyForUserLocked(ActiveAdmin admin, int reqPolicy,
int userId) {
- boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
+ boolean ownsDevice = isDeviceOwner(admin.info.getComponent());
boolean ownsProfile = (getProfileOwner(userId) != null
&& getProfileOwner(userId).getPackageName()
.equals(admin.info.getPackageName()));
- boolean ownsInitialization = isDeviceInitializer(admin.info.getPackageName())
- && !hasUserSetupCompleted(userId);
if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
- if ((userId == UserHandle.USER_SYSTEM && (ownsDevice || ownsInitialization))
- || (ownsDevice && ownsProfile)) {
+ if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || (ownsDevice && ownsProfile)) {
return true;
}
} else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
- if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || ownsProfile
- || ownsInitialization) {
+ if ((userId == UserHandle.USER_SYSTEM && ownsDevice) || ownsProfile) {
return true;
}
} else {
@@ -1798,7 +1909,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
validatePasswordOwnerLocked(policy);
syncDeviceCapabilitiesLocked(policy);
updateMaximumTimeToLockLocked(policy);
- addDeviceInitializerToLockTaskPackagesLocked(userHandle);
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
if (policy.mStatusBarDisabled) {
setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
@@ -1820,8 +1930,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
private void updateDeviceOwnerLocked() {
long ident = mInjector.binderClearCallingIdentity();
try {
- mInjector.getIActivityManager()
- .updateDeviceOwner(getDeviceOwner());
+ if (getDeviceOwner() != null) {
+ mInjector.getIActivityManager()
+ .updateDeviceOwner(getDeviceOwner().getPackageName());
+ }
} catch (RemoteException e) {
// Not gonna happen.
} finally {
@@ -1885,7 +1997,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- public void systemReady(int phase) {
+ @VisibleForTesting
+ void systemReady(int phase) {
if (!mHasFeature) {
return;
}
@@ -2224,7 +2337,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
if (admin.getUid() != mInjector.binderGetCallingUid()) {
// Active device owners must remain active admins.
- if (isDeviceOwner(adminReceiver.getPackageName())) {
+ if (isDeviceOwner(adminReceiver)) {
return;
}
mContext.enforceCallingOrSelfPermission(
@@ -3076,7 +3189,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
- boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwnerOrInitializer(callingUid);
+ boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwner(callingUid);
boolean doNotAskCredentialsOnBoot =
(flags & DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0;
if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) {
@@ -3163,8 +3276,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} else {
// Make sure KEEP_SCREEN_ON is disabled, since that
// would allow bypassing of the maximum time to lock.
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+ mInjector.settingsGlobalPutInt(Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
}
policy.mLastMaximumTimeToLock = timeMs;
@@ -3383,7 +3495,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// If there is a profile owner, redirect to that; otherwise query the device owner.
ComponentName aliasChooser = getProfileOwner(caller.getIdentifier());
if (aliasChooser == null && caller.isOwner()) {
- ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdmin();
+ ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked();
if (deviceOwnerAdmin != null) {
aliasChooser = deviceOwnerAdmin.info.getComponent();
}
@@ -3484,11 +3596,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
long ident = mInjector.binderClearCallingIdentity();
try {
if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) {
- boolean ownsInitialization = isDeviceInitializer(admin.info.getPackageName())
- && !hasUserSetupCompleted(userHandle);
if (userHandle != UserHandle.USER_SYSTEM
- || !(isDeviceOwner(admin.info.getPackageName())
- || ownsInitialization)) {
+ || !isDeviceOwner(admin.info.getComponent())) {
throw new SecurityException(
"Only device owner admins can set WIPE_RESET_PROTECTION_DATA");
}
@@ -3846,16 +3955,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
} catch (NumberFormatException e) {}
}
exclusionList = exclusionList.trim();
- ContentResolver res = mContext.getContentResolver();
ProxyInfo proxyProperties = new ProxyInfo(data[0], proxyPort, exclusionList);
if (!proxyProperties.isValid()) {
Slog.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString());
return;
}
- Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, data[0]);
- Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, proxyPort);
- Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
+ mInjector.settingsGlobalPutString(Settings.Global.GLOBAL_HTTP_PROXY_HOST, data[0]);
+ mInjector.settingsGlobalPutInt(Settings.Global.GLOBAL_HTTP_PROXY_PORT, proxyPort);
+ mInjector.settingsGlobalPutString(Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST,
exclusionList);
}
@@ -4079,8 +4187,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (required) {
long ident = mInjector.binderClearCallingIdentity();
try {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.AUTO_TIME, 1 /* AUTO_TIME on */);
+ mInjector.settingsGlobalPutInt(Settings.Global.AUTO_TIME, 1 /* AUTO_TIME on */);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
@@ -4096,7 +4203,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return false;
}
synchronized (this) {
- ActiveAdmin deviceOwner = getDeviceOwnerAdmin();
+ ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked();
return (deviceOwner != null) ? deviceOwner.requireAutoTime : false;
}
}
@@ -4239,13 +4346,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
- public boolean setDeviceOwner(String packageName, String ownerName, int userId) {
+ public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId) {
if (!mHasFeature) {
return false;
}
- if (packageName == null
- || !isPackageInstalledForUser(packageName, userId)) {
- throw new IllegalArgumentException("Invalid package name " + packageName
+ if (admin == null
+ || !isPackageInstalledForUser(admin.getPackageName(), userId)) {
+ throw new IllegalArgumentException("Invalid component " + admin
+ " for device owner");
}
synchronized (this) {
@@ -4261,7 +4368,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mInjector.binderRestoreCallingIdentity(ident);
}
- mOwners.setDeviceOwner(packageName, ownerName, userId);
+ mOwners.setDeviceOwner(admin, ownerName, userId);
mOwners.writeDeviceOwner();
updateDeviceOwnerLocked();
Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
@@ -4277,24 +4384,33 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ public boolean isDeviceOwner(ComponentName who) {
+ if (!mHasFeature) {
+ return false;
+ }
+ synchronized (this) {
+ return mOwners.hasDeviceOwner() && mOwners.getDeviceOwnerComponent().equals(who);
+ }
+ }
+
@Override
- public boolean isDeviceOwner(String packageName) {
+ public boolean isDeviceOwnerPackage(String packageName) {
if (!mHasFeature) {
return false;
}
synchronized (this) {
return mOwners.hasDeviceOwner()
- && mOwners.getDeviceOwnerPackageName().equals(packageName);
+ && mOwners.getDeviceOwnerComponent().getPackageName().equals(packageName);
}
}
@Override
- public String getDeviceOwner() {
+ public ComponentName getDeviceOwner() {
if (!mHasFeature) {
return null;
}
synchronized (this) {
- return mOwners.getDeviceOwnerPackageName();
+ return mOwners.getDeviceOwnerComponent();
}
}
@@ -4317,17 +4433,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
// Returns the active device owner or null if there is no device owner.
- private ActiveAdmin getDeviceOwnerAdmin() {
- String deviceOwnerPackageName = getDeviceOwner();
- if (deviceOwnerPackageName == null) {
+ @VisibleForTesting
+ ActiveAdmin getDeviceOwnerAdminLocked() {
+ ComponentName component = getDeviceOwner();
+ if (component == null) {
return null;
}
- DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
+ DevicePolicyData policy = getUserData(mOwners.getDeviceOwnerUserId());
final int n = policy.mAdminList.size();
for (int i = 0; i < n; i++) {
ActiveAdmin admin = policy.mAdminList.get(i);
- if (deviceOwnerPackageName.equals(admin.info.getPackageName())) {
+ if (component.equals(admin.info.getComponent())) {
return admin;
}
}
@@ -4337,15 +4454,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void clearDeviceOwner(String packageName) {
Preconditions.checkNotNull(packageName, "packageName is null");
+ final int callingUid = mInjector.binderGetCallingUid();
try {
int uid = mContext.getPackageManager().getPackageUid(packageName, 0);
- if (uid != mInjector.binderGetCallingUid()) {
+ if (uid != callingUid) {
throw new SecurityException("Invalid packageName");
}
} catch (NameNotFoundException e) {
throw new SecurityException(e);
}
- if (!isDeviceOwner(packageName)) {
+ if (!mOwners.hasDeviceOwner() || !getDeviceOwner().getPackageName().equals(packageName)
+ || (mOwners.getDeviceOwnerUserId() != UserHandle.getUserId(callingUid))) {
throw new SecurityException("clearDeviceOwner can only be called by the device owner");
}
synchronized (this) {
@@ -4367,126 +4486,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
- public boolean setDeviceInitializer(ComponentName who, ComponentName initializer) {
- if (!mHasFeature) {
- return false;
- }
- if (initializer == null ||
- !mOwners.hasDeviceOwner() ||
- !isPackageInstalledForUser(initializer.getPackageName(),
- mOwners.getDeviceOwnerUserId())) {
- throw new IllegalArgumentException("Invalid component name " + initializer
- + " for device initializer or no device owner set");
- }
- boolean isInitializerSystemApp;
- try {
- isInitializerSystemApp = isSystemApp(mIPackageManager,
- initializer.getPackageName(),
- mInjector.binderGetCallingUserHandle().getIdentifier());
- } catch (RemoteException | IllegalArgumentException e) {
- isInitializerSystemApp = false;
- Slog.e(LOG_TAG, "Fail to check if device initialzer is system app.", e);
- }
- if (!isInitializerSystemApp) {
- throw new IllegalArgumentException("Only system app can be set as device initializer.");
- }
- synchronized (this) {
- enforceCanSetDeviceInitializer(who);
-
- if (mOwners.hasDeviceInitializer()) {
- throw new IllegalStateException(
- "Trying to set device initializer but device initializer is already set.");
- }
-
- mOwners.setDeviceInitializer(initializer);
-
- addDeviceInitializerToLockTaskPackagesLocked(mOwners.getDeviceOwnerUserId());
- mOwners.writeDeviceOwner();
- return true;
- }
- }
-
- private void enforceCanSetDeviceInitializer(ComponentName who) {
- if (who == null) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
- if (hasUserSetupCompleted(UserHandle.USER_SYSTEM)) {
- throw new IllegalStateException(
- "Trying to set device initializer but device is already provisioned.");
- }
- } else {
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
- }
- }
-
- @Override
- public boolean isDeviceInitializer(String packageName) {
- if (!mHasFeature) {
- return false;
- }
- synchronized (this) {
- return mOwners.hasDeviceInitializer()
- && mOwners.getDeviceInitializerPackageName().equals(packageName);
- }
- }
-
- @Override
- public String getDeviceInitializer() {
- if (!mHasFeature) {
- return null;
- }
- synchronized (this) {
- if (mOwners.hasDeviceInitializer()) {
- return mOwners.getDeviceInitializerPackageName();
- }
- }
- return null;
- }
-
- @Override
- public ComponentName getDeviceInitializerComponent() {
- if (!mHasFeature) {
- return null;
- }
- synchronized (this) {
- if (mOwners.hasDeviceInitializer()) {
- return mOwners.getDeviceInitializerComponent();
- }
- }
- return null;
- }
-
- @Override
- public void clearDeviceInitializer(ComponentName who) {
- if (!mHasFeature) {
- return;
- }
- Preconditions.checkNotNull(who, "ComponentName is null");
-
- ActiveAdmin admin = getActiveAdminUncheckedLocked(who, UserHandle.getCallingUserId());
-
- if (admin.getUid() != mInjector.binderGetCallingUid()) {
- throw new SecurityException("Admin " + who + " is not owned by uid "
- + mInjector.binderGetCallingUid());
- }
-
- if (!isDeviceInitializer(admin.info.getPackageName())
- && !isDeviceOwner(admin.info.getPackageName())) {
- throw new SecurityException(
- "clearDeviceInitializer can only be called by the device initializer/owner");
- }
- synchronized (this) {
- long ident = mInjector.binderClearCallingIdentity();
- try {
- mOwners.clearDeviceInitializer();
- mOwners.writeDeviceOwner();
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
- }
- }
- }
-
- @Override
public boolean setProfileOwner(ComponentName who, String ownerName, int userHandle) {
if (!mHasFeature) {
return false;
@@ -4576,50 +4575,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
@Override
- public boolean setUserEnabled(ComponentName who) {
- if (!mHasFeature) {
- return false;
- }
- synchronized (this) {
- if (who == null) {
- throw new NullPointerException("ComponentName is null");
- }
- int userId = UserHandle.getCallingUserId();
-
- ActiveAdmin activeAdmin =
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- if (!isDeviceInitializer(activeAdmin.info.getPackageName())) {
- throw new SecurityException(
- "This method can only be called by device initializers");
- }
-
- long id = mInjector.binderClearCallingIdentity();
- try {
- if (!isDeviceOwner(activeAdmin.info.getPackageName())) {
- mIPackageManager.setComponentEnabledSetting(who,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP, userId);
-
- removeActiveAdmin(who, userId);
- }
-
- if (userId == UserHandle.USER_SYSTEM) {
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.DEVICE_PROVISIONED, 1);
- }
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 1, userId);
- } catch (RemoteException e) {
- Log.i(LOG_TAG, "Can't talk to package manager", e);
- return false;
- } finally {
- mInjector.binderRestoreCallingIdentity(id);
- }
- return true;
- }
- }
-
- @Override
public void setProfileEnabled(ComponentName who) {
if (!mHasFeature) {
return;
@@ -4674,7 +4629,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// Returns the active profile owner for this user or null if the current user has no
// profile owner.
- private ActiveAdmin getProfileOwnerAdmin(int userHandle) {
+ @VisibleForTesting
+ ActiveAdmin getProfileOwnerAdminLocked(int userHandle) {
ComponentName profileOwner = mOwners.getProfileOwnerComponent(userHandle);
if (profileOwner == null) {
return null;
@@ -5546,12 +5502,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setUserRestriction(ComponentName who, String key, boolean enabled) {
Preconditions.checkNotNull(who, "ComponentName is null");
- final int userHandle = UserHandle.getCallingUserId();
+ final int userHandle = mInjector.userHandleGetCallingUserId();
final UserHandle user = new UserHandle(userHandle);
synchronized (this) {
ActiveAdmin activeAdmin =
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- boolean isDeviceOwner = isDeviceOwner(activeAdmin.info.getPackageName());
+ boolean isDeviceOwner = isDeviceOwner(who);
if (!isDeviceOwner && userHandle != UserHandle.USER_SYSTEM
&& DEVICE_OWNER_USER_RESTRICTIONS.contains(key)) {
throw new SecurityException("Profile owners cannot set user restriction " + key);
@@ -5571,37 +5527,40 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mInjector.getIAudioService()
.setMasterMute(true, 0, mContext.getPackageName(), userHandle);
} else if (UserManager.DISALLOW_CONFIG_WIFI.equals(key)) {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ mInjector.settingsSecurePutIntForUser(
Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0,
userHandle);
} else if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ mInjector.settingsSecurePutIntForUser(
Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF,
userHandle);
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ mInjector.settingsSecurePutStringForUser(
Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "",
userHandle);
} else if (UserManager.DISALLOW_DEBUGGING_FEATURES.equals(key)) {
// Only disable adb if changing for system user, since it is global
// TODO: should this be admin user?
if (userHandle == UserHandle.USER_SYSTEM) {
- Settings.Global.putStringForUser(mContext.getContentResolver(),
+ mInjector.settingsGlobalPutStringForUser(
Settings.Global.ADB_ENABLED, "0", userHandle);
}
} else if (UserManager.ENSURE_VERIFY_APPS.equals(key)) {
- Settings.Global.putStringForUser(mContext.getContentResolver(),
+ mInjector.settingsGlobalPutStringForUser(
Settings.Global.PACKAGE_VERIFIER_ENABLE, "1",
userHandle);
- Settings.Global.putStringForUser(mContext.getContentResolver(),
+ mInjector.settingsGlobalPutStringForUser(
Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, "1",
userHandle);
} else if (UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES.equals(key)) {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ mInjector.settingsSecurePutIntForUser(
Settings.Secure.INSTALL_NON_MARKET_APPS, 0,
userHandle);
}
}
mUserManager.setUserRestriction(key, enabled, user);
+ activeAdmin.ensureUserRestrictions().putBoolean(key, enabled);
+ saveSettingsLocked(userHandle);
+
if (enabled != alreadyRestricted) {
if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) {
// Send out notifications however as some clients may want to reread the
@@ -5902,7 +5861,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// TODO: Should there be a check to make sure this relationship is within a profile group?
//enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
synchronized (this) {
- ActiveAdmin admin = getProfileOwnerAdmin(userId);
+ ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
return (admin != null) ? admin.disableCallerId : false;
}
}
@@ -5995,7 +5954,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
// within a profile group?
// enforceSystemProcess("getCrossProfileCallerIdDisabled can only be called by system");
synchronized (this) {
- ActiveAdmin admin = getProfileOwnerAdmin(userId);
+ ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
return (admin != null) ? admin.disableBluetoothContactSharing : false;
}
}
@@ -6081,7 +6040,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
Bundle adminExtras = new Bundle();
adminExtras.putString(DeviceAdminReceiver.EXTRA_LOCK_TASK_PACKAGE, pkg);
for (ActiveAdmin admin : policy.mAdminList) {
- boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
+ boolean ownsDevice = isDeviceOwner(admin.info.getComponent());
boolean ownsProfile = (getProfileOwner(userHandle) != null
&& getProfileOwner(userHandle).equals(admin.info.getPackageName()));
if (ownsDevice || ownsProfile) {
@@ -6098,7 +6057,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
@Override
public void setGlobalSetting(ComponentName who, String setting, String value) {
- final ContentResolver contentResolver = mContext.getContentResolver();
Preconditions.checkNotNull(who, "ComponentName is null");
synchronized (this) {
@@ -6126,7 +6084,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
long id = mInjector.binderClearCallingIdentity();
try {
- Settings.Global.putString(contentResolver, setting, value);
+ mInjector.settingsGlobalPutString(setting, value);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -6140,10 +6098,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
final ContentResolver contentResolver = mContext.getContentResolver();
synchronized (this) {
- ActiveAdmin activeAdmin =
- getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
- if (isDeviceOwner(activeAdmin.info.getPackageName())) {
+ if (isDeviceOwner(who)) {
if (!SECURE_SETTINGS_DEVICEOWNER_WHITELIST.contains(setting)) {
throw new SecurityException(String.format(
"Permission denial: Device owners cannot update %1$s", setting));
@@ -6155,7 +6112,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
long id = mInjector.binderClearCallingIdentity();
try {
- Settings.Secure.putStringForUser(contentResolver, setting, value, callingUserId);
+ mInjector.settingsSecurePutStringForUser(setting, value, callingUserId);
} finally {
mInjector.binderRestoreCallingIdentity(id);
}
@@ -6278,18 +6235,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
*/
void updateUserSetupComplete() {
List<UserInfo> users = mUserManager.getUsers(true);
- ContentResolver resolver = mContext.getContentResolver();
final int N = users.size();
for (int i = 0; i < N; i++) {
int userHandle = users.get(i).id;
- if (Settings.Secure.getIntForUser(resolver, Settings.Secure.USER_SETUP_COMPLETE, 0,
+ if (mInjector.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0,
userHandle) != 0) {
DevicePolicyData policy = getUserData(userHandle);
if (!policy.mUserSetupComplete) {
policy.mUserSetupComplete = true;
synchronized (this) {
- // The DeviceInitializer was whitelisted but now should be removed.
- removeDeviceInitializerFromLockTaskPackages(userHandle);
saveSettingsLocked(userHandle);
}
}
@@ -6297,35 +6251,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
- private void addDeviceInitializerToLockTaskPackagesLocked(int userHandle) {
- if (hasUserSetupCompleted(userHandle)) {
- return;
- }
-
- final String deviceInitializerPackage = getDeviceInitializer();
- if (deviceInitializerPackage == null) {
- return;
- }
-
- final List<String> packages = getLockTaskPackagesLocked(userHandle);
- if (!packages.contains(deviceInitializerPackage)) {
- packages.add(deviceInitializerPackage);
- setLockTaskPackagesLocked(userHandle, packages);
- }
- }
-
- private void removeDeviceInitializerFromLockTaskPackages(int userHandle) {
- final String deviceInitializerPackage = getDeviceInitializer();
- if (deviceInitializerPackage == null) {
- return;
- }
-
- List<String> packages = getLockTaskPackagesLocked(userHandle);
- if (packages.remove(deviceInitializerPackage)) {
- setLockTaskPackagesLocked(userHandle, packages);
- }
- }
-
private class SetupContentObserver extends ContentObserver {
private final Uri mUserSetupComplete = Settings.Secure.getUriFor(
@@ -6450,15 +6375,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
/**
- * Checks if the caller of the method is the device owner app or device initialization app.
+ * Checks if the caller of the method is the device owner app.
*
* @param callerUid UID of the caller.
- * @return true if the caller is the device owner app or device initializer.
+ * @return true if the caller is the device owner app
*/
- private boolean isCallerDeviceOwnerOrInitializer(int callerUid) {
+ private boolean isCallerDeviceOwner(int callerUid) {
String[] pkgs = mContext.getPackageManager().getPackagesForUid(callerUid);
for (String pkg : pkgs) {
- if (isDeviceOwner(pkg) || isDeviceInitializer(pkg)) {
+ if (isDeviceOwnerPackage(pkg)) {
return true;
}
}
@@ -6480,7 +6405,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
updateReceivedTime);
synchronized (this) {
- String deviceOwnerPackage = getDeviceOwner();
+ final String deviceOwnerPackage = getDeviceOwner() == null ? null :
+ getDeviceOwner().getPackageName();
if (deviceOwnerPackage == null) {
return;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 370cf48b4655..799267d9bf44 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -16,16 +16,11 @@
package com.android.server.devicepolicy;
-import android.app.AppGlobals;
import android.app.admin.SystemUpdatePolicy;
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.os.Environment;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
@@ -35,6 +30,7 @@ import android.util.Slog;
import android.util.Xml;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Preconditions;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -90,9 +86,6 @@ class Owners {
private int mDeviceOwnerUserId = UserHandle.USER_NULL;
- // Internal state for the device initializer package.
- private OwnerInfo mDeviceInitializer;
-
// Internal state for the profile owner packages.
private final ArrayMap<Integer, OwnerInfo> mProfileOwners = new ArrayMap<>();
@@ -153,12 +146,16 @@ class Owners {
return mDeviceOwner != null ? mDeviceOwner.name : null;
}
- void setDeviceOwner(String packageName, String ownerName, int userId) {
+ ComponentName getDeviceOwnerComponent() {
+ return mDeviceOwner != null ? mDeviceOwner.admin : null;
+ }
+
+ void setDeviceOwner(ComponentName admin, String ownerName, int userId) {
if (userId < 0) {
Slog.e(TAG, "Invalid user id for device owner user: " + userId);
return;
}
- mDeviceOwner = new OwnerInfo(ownerName, packageName);
+ mDeviceOwner = new OwnerInfo(ownerName, admin);
mDeviceOwnerUserId = userId;
}
@@ -167,26 +164,6 @@ class Owners {
mDeviceOwnerUserId = UserHandle.USER_NULL;
}
- ComponentName getDeviceInitializerComponent() {
- return mDeviceInitializer.admin;
- }
-
- String getDeviceInitializerPackageName() {
- return mDeviceInitializer != null ? mDeviceInitializer.packageName : null;
- }
-
- void setDeviceInitializer(ComponentName admin) {
- mDeviceInitializer = new OwnerInfo(null, admin);
- }
-
- void clearDeviceInitializer() {
- mDeviceInitializer = null;
- }
-
- boolean hasDeviceInitializer() {
- return mDeviceInitializer != null;
- }
-
void setProfileOwner(ComponentName admin, String ownerName, int userId) {
mProfileOwners.put(userId, new OwnerInfo(ownerName, admin));
}
@@ -252,18 +229,7 @@ class Owners {
mDeviceOwner = new OwnerInfo(name, packageName);
mDeviceOwnerUserId = UserHandle.USER_SYSTEM;
} else if (tag.equals(TAG_DEVICE_INITIALIZER)) {
- String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
- String initializerComponentStr =
- parser.getAttributeValue(null, ATTR_COMPONENT_NAME);
- ComponentName admin =
- ComponentName.unflattenFromString(initializerComponentStr);
- if (admin != null) {
- mDeviceInitializer = new OwnerInfo(null, admin);
- } else {
- mDeviceInitializer = new OwnerInfo(null, packageName);
- Slog.e(TAG, "Error parsing device-owner file. Bad component name " +
- initializerComponentStr);
- }
+ // Deprecated tag
} else if (tag.equals(TAG_PROFILE_OWNER)) {
String profileOwnerPackageName = parser.getAttributeValue(null, ATTR_PACKAGE);
String profileOwnerName = parser.getAttributeValue(null, ATTR_NAME);
@@ -444,8 +410,7 @@ class Owners {
@Override
boolean shouldWrite() {
- return (mDeviceOwner != null) || (mDeviceInitializer != null)
- || (mSystemUpdatePolicy != null);
+ return (mDeviceOwner != null) || (mSystemUpdatePolicy != null);
}
@Override
@@ -457,9 +422,6 @@ class Owners {
out.endTag(null, TAG_DEVICE_OWNER_CONTEXT);
}
- if (mDeviceInitializer != null) {
- mDeviceInitializer.writeToXml(out, TAG_DEVICE_INITIALIZER);
- }
if (mSystemUpdatePolicy != null) {
out.startTag(null, TAG_SYSTEM_UPDATE_POLICY);
mSystemUpdatePolicy.saveToXml(out);
@@ -488,7 +450,7 @@ class Owners {
break;
}
case TAG_DEVICE_INITIALIZER:
- mDeviceInitializer = OwnerInfo.readFromXml(parser);
+ // Deprecated tag
break;
case TAG_SYSTEM_UPDATE_POLICY:
mSystemUpdatePolicy = SystemUpdatePolicy.restoreFromXml(parser);
@@ -584,7 +546,7 @@ class Owners {
} else {
// This shouldn't happen but switch from package name -> component name
// might have written bad device owner files. b/17652534
- Slog.e(TAG, "Error parsing device-owner file. Bad component name " +
+ Slog.e(TAG, "Error parsing owner file. Bad component name " +
componentName);
}
}
@@ -607,11 +569,6 @@ class Owners {
pw.println(prefix + " User ID: " + mDeviceOwnerUserId);
pw.println();
}
- if (mDeviceInitializer != null) {
- pw.println(prefix + "Device Initializer: ");
- mDeviceInitializer.dump(prefix + " ", pw);
- pw.println();
- }
if (mSystemUpdatePolicy != null) {
pw.println(prefix + "System Update Policy: " + mSystemUpdatePolicy);
pw.println();
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index e0d2ac12be21..1a728a0ca261 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -299,6 +299,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
Os.setsockoptIfreq(mUdpSock, SOL_SOCKET, SO_BINDTODEVICE, mIfaceName);
Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
+ Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0);
Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT);
NetworkUtils.protectFromVpn(mUdpSock);
} catch(SocketException|ErrnoException e) {
@@ -308,6 +309,16 @@ public class DhcpClient extends BaseDhcpStateMachine {
return true;
}
+ private boolean connectUdpSock(Inet4Address to) {
+ try {
+ Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER);
+ return true;
+ } catch (SocketException|ErrnoException e) {
+ Log.e(TAG, "Error connecting UDP socket", e);
+ return false;
+ }
+ }
+
private static void closeQuietly(FileDescriptor fd) {
try {
IoBridge.closeAndSignalBlockedThreads(fd);
@@ -325,7 +336,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
try {
mNMService.setInterfaceConfig(mIfaceName, ifcg);
} catch (RemoteException|IllegalStateException e) {
- Log.e(TAG, "Error configuring IP address : " + e);
+ Log.e(TAG, "Error configuring IP address " + address + ": ", e);
return false;
}
return true;
@@ -376,8 +387,10 @@ public class DhcpClient extends BaseDhcpStateMachine {
maybeLog("Broadcasting " + description);
Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
} else {
- maybeLog("Unicasting " + description + " to " + to.getHostAddress());
- Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
+ // It's safe to call getpeername here, because we only send unicast packets if we
+ // have an IP address, and we connect the UDP socket in DhcpHaveAddressState#enter.
+ maybeLog("Unicasting " + description + " to " + Os.getpeername(mUdpSock));
+ Os.write(mUdpSock, buf);
}
} catch(ErrnoException|IOException e) {
Log.e(TAG, "Can't send packet: ", e);
@@ -789,6 +802,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
transitionTo(mDhcpBoundState);
}
} else if (packet instanceof DhcpNakPacket) {
+ // TODO: Wait a while before returning into INIT state.
Log.d(TAG, "Received NAK, returning to INIT");
mOffer = null;
transitionTo(mDhcpInitState);
@@ -806,10 +820,8 @@ public class DhcpClient extends BaseDhcpStateMachine {
@Override
public void enter() {
super.enter();
- if (setIpAddress(mDhcpLease.ipAddress)) {
- maybeLog("Configured IP address " + mDhcpLease.ipAddress);
- } else {
- Log.e(TAG, "Failed to configure IP address " + mDhcpLease.ipAddress);
+ if (!setIpAddress(mDhcpLease.ipAddress) ||
+ !connectUdpSock((mDhcpLease.serverAddress))) {
notifyFailure();
// There's likely no point in going into DhcpInitState here, we'll probably just
// repeat the transaction, get the same IP address as before, and fail.
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 982bae0ccb42..53f55cdc847f 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -423,7 +423,7 @@ public class IpReachabilityMonitor {
try {
byteBuffer = recvKernelReply();
} catch (ErrnoException e) {
- Log.w(TAG, "ErrnoException: ", e);
+ if (stillRunning()) { Log.w(TAG, "ErrnoException: ", e); }
break;
}
final long whenMs = SystemClock.elapsedRealtime();
diff --git a/services/tests/servicestests/src/com/android/server/MountServiceTests.java b/services/tests/servicestests/src/com/android/server/MountServiceTests.java
index 9c88b406ccf3..ecfe0db103fe 100644
--- a/services/tests/servicestests/src/com/android/server/MountServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/MountServiceTests.java
@@ -27,8 +27,6 @@ import android.test.ComparisonFailure;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
-import static com.android.server.MountService.buildObbPath;
-
import com.android.frameworks.servicestests.R;
import java.io.File;
@@ -42,16 +40,6 @@ public class MountServiceTests extends AndroidTestCase {
private static final String OBB_MOUNT_PREFIX = "/mnt/obb/";
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
private static void assertStartsWith(String message, String prefix, String actual) {
if (!actual.startsWith(prefix)) {
throw new ComparisonFailure(message, prefix, actual);
@@ -195,8 +183,7 @@ public class MountServiceTests extends AndroidTestCase {
final ObbObserver observer = new ObbObserver();
assertTrue("unmountObb call on test1.obb should succeed",
- sm.unmountObb(file.getPath(),
- false, observer));
+ sm.unmountObb(file.getPath(), false, observer));
assertTrue("Unmount should have completed",
observer.waitForCompletion());
@@ -284,34 +271,4 @@ public class MountServiceTests extends AndroidTestCase {
unmountObb(sm, file1, OnObbStateChangeListener.UNMOUNTED);
unmountObb(sm, file2, OnObbStateChangeListener.UNMOUNTED);
}
-
- public void testBuildObbPath() {
- final int userId = 10;
-
- // Paths outside external storage should remain untouched
- assertEquals("/storage/random/foo",
- buildObbPath("/storage/random/foo", userId, true));
- assertEquals("/storage/random/foo",
- buildObbPath("/storage/random/foo", userId, false));
-
- // Paths on user-specific emulated storage
- assertEquals("/mnt/shell/emulated/10/foo",
- buildObbPath("/storage/emulated_legacy/foo", userId, true));
- assertEquals("/storage/emulated/10/foo",
- buildObbPath("/storage/emulated_legacy/foo", userId, false));
- assertEquals("/mnt/shell/emulated/10/foo",
- buildObbPath("/storage/emulated/10/foo", userId, true));
- assertEquals("/storage/emulated/10/foo",
- buildObbPath("/storage/emulated/10/foo", userId, false));
-
- // Paths on shared OBB emulated storage
- assertEquals("/mnt/shell/emulated/obb/foo",
- buildObbPath("/storage/emulated_legacy/Android/obb/foo", userId, true));
- assertEquals("/storage/emulated/0/Android/obb/foo",
- buildObbPath("/storage/emulated_legacy/Android/obb/foo", userId, false));
- assertEquals("/mnt/shell/emulated/obb/foo",
- buildObbPath("/storage/emulated/10/Android/obb/foo", userId, true));
- assertEquals("/storage/emulated/0/Android/obb/foo",
- buildObbPath("/storage/emulated/10/Android/obb/foo", userId, false));
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index c6c74979f6ef..b109e7bc9d03 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -50,11 +50,11 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
private final File mDeviceOwnerFile;
private final File mProfileOwnerBase;
- public OwnersTestable(Context context, File dataDir) {
+ public OwnersTestable(DpmMockContext context) {
super(context);
- mLegacyFile = new File(dataDir, LEGACY_FILE);
- mDeviceOwnerFile = new File(dataDir, DEVICE_OWNER_FILE);
- mProfileOwnerBase = new File(dataDir, PROFILE_OWNER_FILE_BASE);
+ mLegacyFile = new File(context.dataDir, LEGACY_FILE);
+ mDeviceOwnerFile = new File(context.dataDir, DEVICE_OWNER_FILE);
+ mProfileOwnerBase = new File(context.dataDir, PROFILE_OWNER_FILE_BASE);
}
@Override
@@ -90,27 +90,15 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
public final File dataDir;
- public final File systemUserDataDir;
- public final File secondUserDataDir;
-
private MockInjector(DpmMockContext context, File dataDir) {
super(context);
this.context = context;
this.dataDir = dataDir;
-
- systemUserDataDir = new File(dataDir, "user0");
- DpmTestUtils.clearDir(dataDir);
-
- secondUserDataDir = new File(dataDir, "user" + DpmMockContext.CALLER_USER_HANDLE);
- DpmTestUtils.clearDir(secondUserDataDir);
-
- when(context.environment.getUserSystemDirectory(
- eq(DpmMockContext.CALLER_USER_HANDLE))).thenReturn(secondUserDataDir);
}
@Override
Owners newOwners() {
- return new OwnersTestable(context, dataDir);
+ return new OwnersTestable(context);
}
@Override
@@ -165,7 +153,7 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
@Override
String getDevicePolicyFilePathForSystemUser() {
- return systemUserDataDir.getAbsolutePath();
+ return context.systemUserDataDir.getAbsolutePath();
}
@Override
@@ -237,5 +225,45 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi
boolean userManagerIsSplitSystemUser() {
return context.userManagerForMock.isSplitSystemUser();
}
+
+ @Override
+ int settingsSecureGetIntForUser(String name, int def, int userHandle) {
+ return context.settings.settingsSecureGetIntForUser(name, def, userHandle);
+ }
+
+ @Override
+ void settingsSecurePutIntForUser(String name, int value, int userHandle) {
+ context.settings.settingsSecurePutIntForUser(name, value, userHandle);
+ }
+
+ @Override
+ void settingsSecurePutStringForUser(String name, String value, int userHandle) {
+ context.settings.settingsSecurePutStringForUser(name, value, userHandle);
+ }
+
+ @Override
+ void settingsGlobalPutStringForUser(String name, String value, int userHandle) {
+ context.settings.settingsGlobalPutStringForUser(name, value, userHandle);
+ }
+
+ @Override
+ void settingsSecurePutInt(String name, int value) {
+ context.settings.settingsSecurePutInt(name, value);
+ }
+
+ @Override
+ void settingsGlobalPutInt(String name, int value) {
+ context.settings.settingsGlobalPutInt(name, value);
+ }
+
+ @Override
+ void settingsSecurePutString(String name, String value) {
+ context.settings.settingsSecurePutString(name, value);
+ }
+
+ @Override
+ void settingsGlobalPutString(String name, String value) {
+ context.settings.settingsGlobalPutString(name, value);
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 0072f526bd95..d6a60c7f3e65 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
import android.Manifest.permission;
import android.app.Activity;
@@ -31,8 +32,8 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.content.pm.PackageInfo;
-import android.content.pm.UserInfo;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Pair;
import org.mockito.ArgumentCaptor;
@@ -44,6 +45,7 @@ import java.util.List;
import java.util.Map;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
@@ -65,8 +67,6 @@ import static org.mockito.Mockito.when;
(mmma frameworks/base/services/tests/servicestests/ for non-ninja build)
*/
public class DevicePolicyManagerTest extends DpmTestBase {
-
-
private DpmMockContext mContext;
public DevicePolicyManager dpm;
public DevicePolicyManagerServiceTestable dpms;
@@ -83,29 +83,46 @@ public class DevicePolicyManagerTest extends DpmTestBase {
when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
.thenReturn(true);
- LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
- dpms = new DevicePolicyManagerServiceTestable(mContext, dataDir);
- dpm = new DevicePolicyManagerTestable(mContext, dpms);
+ initializeDpms();
admin1 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin1.class);
admin2 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin2.class);
admin3 = new ComponentName(mRealTestContext, DummyDeviceAdmins.Admin3.class);
- setUpPackageManagerForAdmin(admin1);
- setUpPackageManagerForAdmin(admin2);
- setUpPackageManagerForAdmin(admin3);
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+ setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
+ setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_UID);
- setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
- setUpPackageInfo();
setUpUserManager();
}
- /**
- * Set up a mock result for {@link PackageManager#queryBroadcastReceivers}. We'll return
- * the actual ResolveInfo for the admin component, but we need to mock PM so it'll return
- * it for user {@link DpmMockContext#CALLER_USER_HANDLE}.
- */
- private void setUpPackageManagerForAdmin(ComponentName admin) {
+ private void initializeDpms() {
+ // Need clearCallingIdentity() to pass permission checks.
+ final long ident = mContext.binder.clearCallingIdentity();
+ try {
+ LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+
+ dpms = new DevicePolicyManagerServiceTestable(mContext, dataDir);
+
+ dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY);
+ dpms.systemReady(SystemService.PHASE_BOOT_COMPLETED);
+
+ dpm = new DevicePolicyManagerTestable(mContext, dpms);
+ } finally {
+ mContext.binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void setUpPackageManagerForAdmin(ComponentName admin, int packageUid) throws Exception {
+ setUpPackageManagerForAdmin(admin, packageUid,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
+ }
+
+ private void setUpPackageManagerForAdmin(ComponentName admin, int packageUid,
+ int enabledSetting) throws Exception {
+
+ // Set up queryBroadcastReceivers().
+
final Intent resolveIntent = new Intent();
resolveIntent.setComponent(admin);
final List<ResolveInfo> realResolveInfo =
@@ -115,51 +132,46 @@ public class DevicePolicyManagerTest extends DpmTestBase {
assertNotNull(realResolveInfo);
assertEquals(1, realResolveInfo.size());
+ // We need to change AI, so set a clone.
+ realResolveInfo.set(0, DpmTestUtils.cloneParcelable(realResolveInfo.get(0)));
+
// We need to rewrite the UID in the activity info.
- realResolveInfo.get(0).activityInfo.applicationInfo.uid = DpmMockContext.CALLER_UID;
+ realResolveInfo.get(0).activityInfo.applicationInfo.uid = packageUid;
doReturn(realResolveInfo).when(mContext.packageManager).queryBroadcastReceivers(
MockUtils.checkIntentComponent(admin),
eq(PackageManager.GET_META_DATA
| PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
- eq(DpmMockContext.CALLER_USER_HANDLE)
- );
- }
+ eq(UserHandle.getUserId(packageUid)));
- /**
- * Set up a mock result for {@link IPackageManager#getApplicationInfo} for user
- * {@link DpmMockContext#CALLER_USER_HANDLE}.
- */
- private void setUpApplicationInfo(int enabledSetting) throws Exception {
- final ApplicationInfo ai = mRealTestContext.getPackageManager().getApplicationInfo(
- admin1.getPackageName(),
- PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
+ // Set up getApplicationInfo().
+
+ final ApplicationInfo ai = DpmTestUtils.cloneParcelable(
+ mRealTestContext.getPackageManager().getApplicationInfo(
+ admin1.getPackageName(),
+ PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS));
ai.enabledSetting = enabledSetting;
+ ai.uid = packageUid;
doReturn(ai).when(mContext.ipackageManager).getApplicationInfo(
eq(admin1.getPackageName()),
eq(PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS),
- eq(DpmMockContext.CALLER_USER_HANDLE));
- }
+ eq(UserHandle.getUserId(packageUid)));
- /**
- * Set up a mock result for {@link IPackageManager#getPackageInfo(String, int, int)} for user
- * {@link DpmMockContext#CALLER_USER_HANDLE} as well as the system user.
- */
- private void setUpPackageInfo() throws Exception {
- final PackageInfo pi = mRealTestContext.getPackageManager().getPackageInfo(
- admin1.getPackageName(), 0);
+ // Set up getPackageInfo().
+
+ final PackageInfo pi = DpmTestUtils.cloneParcelable(
+ mRealTestContext.getPackageManager().getPackageInfo(
+ admin1.getPackageName(), 0));
assertTrue(pi.applicationInfo.flags != 0);
+ pi.applicationInfo.uid = packageUid;
+
doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
eq(admin1.getPackageName()),
eq(0),
- eq(DpmMockContext.CALLER_USER_HANDLE));
- doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
- eq(admin1.getPackageName()),
- eq(0),
- eq(UserHandle.USER_SYSTEM));
+ eq(UserHandle.getUserId(packageUid)));
}
private void setUpUserManager() {
@@ -193,25 +205,15 @@ public class DevicePolicyManagerTest extends DpmTestBase {
}).when(mContext.userManager).getApplicationRestrictions(
anyString(), any(UserHandle.class));
- // System user is always running.
- when(mContext.userManager.isUserRunning(MockUtils.checkUserHandle(UserHandle.USER_SYSTEM)))
- .thenReturn(true);
-
- // Set up (default) UserInfo for CALLER_USER_HANDLE.
- final UserInfo uh = new UserInfo(DpmMockContext.CALLER_USER_HANDLE,
- "user" + DpmMockContext.CALLER_USER_HANDLE, 0);
-
- when(mContext.userManager.getUserInfo(eq(DpmMockContext.CALLER_USER_HANDLE)))
- .thenReturn(uh);
+ // Add the first secondary user.
+ mContext.addUser(DpmMockContext.CALLER_USER_HANDLE, 0);
}
private void setAsProfileOwner(ComponentName admin) {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
- final UserInfo uh = new UserInfo(DpmMockContext.CALLER_USER_HANDLE, "user", 0);
-
- // DO needs to be an DA.
+ // PO needs to be an DA.
dpm.setActiveAdmin(admin, /* replace =*/ false);
// Fire!
@@ -309,7 +311,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Next, add one more admin.
// Before doing so, update the application info, now it's enabled.
- setUpApplicationInfo(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+ setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
dpm.setActiveAdmin(admin2, /* replace =*/ false);
@@ -354,6 +357,33 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.callerPermissions.remove("android.permission.INTERACT_ACROSS_USERS_FULL");
}
+ public void testSetActiveAdmin_multiUsers() throws Exception {
+
+ final int ANOTHER_USER_ID = 100;
+ final int ANOTHER_ADMIN_UID = UserHandle.getUid(ANOTHER_USER_ID, 20456);
+
+ mMockContext.addUser(ANOTHER_USER_ID, 0); // Add one more user.
+
+ // Set up pacakge manager for the other user.
+ setUpPackageManagerForAdmin(admin2, ANOTHER_ADMIN_UID);
+
+ mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+ mMockContext.binder.callingUid = ANOTHER_ADMIN_UID;
+ dpm.setActiveAdmin(admin2, /* replace =*/ false);
+
+
+ mMockContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ assertTrue(dpm.isAdminActive(admin1));
+ assertFalse(dpm.isAdminActive(admin2));
+
+ mMockContext.binder.callingUid = ANOTHER_ADMIN_UID;
+ assertFalse(dpm.isAdminActive(admin1));
+ assertTrue(dpm.isAdminActive(admin2));
+ }
+
/**
* Test for:
* {@link DevicePolicyManager#setActiveAdmin}
@@ -400,9 +430,11 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// having MANAGE_DEVICE_ADMINS.
mContext.callerPermissions.clear();
+ // Change the caller, and call into DPMS directly with a different user-id.
+
mContext.binder.callingUid = 1234567;
try {
- dpm.removeActiveAdmin(admin1);
+ dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE);
fail("Didn't throw SecurityException");
} catch (SecurityException expected) {
}
@@ -412,7 +444,7 @@ public class DevicePolicyManagerTest extends DpmTestBase {
* Test for:
* {@link DevicePolicyManager#removeActiveAdmin}
*/
- public void testRemoveActiveAdmin_fromDifferentUserWithMINTERACT_ACROSS_USERS_FULL() {
+ public void testRemoveActiveAdmin_fromDifferentUserWithINTERACT_ACROSS_USERS_FULL() {
mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
// Add admin1.
@@ -424,8 +456,11 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Different user, but should work, because caller has proper permissions.
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ // Change the caller, and call into DPMS directly with a different user-id.
mContext.binder.callingUid = 1234567;
- dpm.removeActiveAdmin(admin1);
+
+ dpms.removeActiveAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE);
assertTrue(dpm.isRemovingAdmin(admin1, DpmMockContext.CALLER_USER_HANDLE));
@@ -498,14 +533,17 @@ public class DevicePolicyManagerTest extends DpmTestBase {
mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
- // Call from a process on the system user.
+ // In this test, change the caller user to "system".
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ // Make sure admin1 is installed on system user.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+
// DO needs to be an DA.
dpm.setActiveAdmin(admin1, /* replace =*/ false);
// Fire!
- assertTrue(dpm.setDeviceOwner(admin1.getPackageName(), "owner-name"));
+ assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
// Verify internal calls.
verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
@@ -536,10 +574,8 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// Call from a process on the system user.
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
- // DO needs to be an DA.
- dpm.setActiveAdmin(admin1, /* replace =*/ false);
try {
- dpm.setDeviceOwner("a.b.c");
+ dpm.setDeviceOwner(new ComponentName("a.b.c", ".def"));
fail("Didn't throw IllegalArgumentException");
} catch (IllegalArgumentException expected) {
}
@@ -549,6 +585,85 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// TODO Test more failure cases. Basically test all chacks in enforceCanSetDeviceOwner().
}
+ public void testClearDeviceOwner() throws Exception {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ // Set admin1 as a DA to the secondary user.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+ // Set admin 1 as the DO to the system user.
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
+
+ // Verify internal calls.
+ verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
+ eq(admin1.getPackageName()));
+
+ assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+
+ // Set up other mocks.
+ when(mContext.userManager.getUserRestrictions()).thenReturn(new Bundle());
+
+ // Now call clear.
+ doReturn(DpmMockContext.CALLER_SYSTEM_USER_UID).when(mContext.packageManager).getPackageUid(
+ eq(admin1.getPackageName()),
+ anyInt());
+ dpm.clearDeviceOwnerApp(admin1.getPackageName());
+
+ // Now DO shouldn't be set.
+ assertNull(dpm.getDeviceOwner());
+
+ // TODO Check other calls.
+ }
+
+ public void testClearDeviceOwner_fromDifferentUser() throws Exception {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ // Set admin1 as a DA to the secondary user.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+
+ // Set admin 1 as the DO to the system user.
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ assertTrue(dpm.setDeviceOwner(admin1, "owner-name"));
+
+ // Verify internal calls.
+ verify(mContext.iactivityManager, times(1)).updateDeviceOwner(
+ eq(admin1.getPackageName()));
+
+ assertEquals(admin1.getPackageName(), dpm.getDeviceOwner());
+
+ // Now call clear from the secondary user, which should throw.
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+
+ // Now call clear.
+ doReturn(DpmMockContext.CALLER_UID).when(mContext.packageManager).getPackageUid(
+ eq(admin1.getPackageName()),
+ anyInt());
+ try {
+ dpm.clearDeviceOwnerApp(admin1.getPackageName());
+ fail("Didn't throw");
+ } catch (SecurityException e) {
+ assertEquals("clearDeviceOwner can only be called by the device owner", e.getMessage());
+ }
+
+ // Now DO shouldn't be set.
+ assertNotNull(dpm.getDeviceOwner());
+ }
+
public void testSetProfileOwner() throws Exception {
setAsProfileOwner(admin1);
}
@@ -557,6 +672,85 @@ public class DevicePolicyManagerTest extends DpmTestBase {
// TODO Test more failure cases. Basically test all chacks in enforceCanSetProfileOwner().
}
+ public void testGetDeviceOwnerAdminLocked() throws Exception {
+ checkDeviceOwnerWithMultipleDeviceAdmins();
+ }
+
+ private void checkDeviceOwnerWithMultipleDeviceAdmins() throws Exception {
+ // In ths test, we use 3 users (system + 2 secondary users), set some device admins to them,
+ // set admin2 on CALLER_USER_HANDLE as DO, then call getDeviceOwnerAdminLocked() to
+ // make sure it gets the right component from the right user.
+
+ final int ANOTHER_USER_ID = 100;
+ final int ANOTHER_ADMIN_UID = UserHandle.getUid(ANOTHER_USER_ID, 456);
+
+ mMockContext.addUser(ANOTHER_USER_ID, 0); // Add one more user.
+
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ // Make sure the admin packge is installed to each user.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ setUpPackageManagerForAdmin(admin3, DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+ setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID);
+
+ setUpPackageManagerForAdmin(admin2, ANOTHER_ADMIN_UID);
+
+
+ // Set active admins to the users.
+ dpm.setActiveAdmin(admin1, /* replace =*/ false);
+ dpm.setActiveAdmin(admin3, /* replace =*/ false);
+
+ dpm.setActiveAdmin(admin1, /* replace =*/ false, DpmMockContext.CALLER_USER_HANDLE);
+ dpm.setActiveAdmin(admin2, /* replace =*/ false, DpmMockContext.CALLER_USER_HANDLE);
+
+ dpm.setActiveAdmin(admin2, /* replace =*/ false, ANOTHER_USER_ID);
+
+ // Set DO on the first non-system user.
+ mContext.setUserRunning(DpmMockContext.CALLER_USER_HANDLE, true);
+ assertTrue(dpm.setDeviceOwner(admin2, "owner-name", DpmMockContext.CALLER_USER_HANDLE));
+
+ // Make sure it's set.
+ assertEquals(admin2, dpm.getDeviceOwnerComponent());
+
+ // Then check getDeviceOwnerAdminLocked().
+ assertEquals(admin2, dpms.getDeviceOwnerAdminLocked().info.getComponent());
+ assertEquals(DpmMockContext.CALLER_UID, dpms.getDeviceOwnerAdminLocked().getUid());
+ }
+
+ /**
+ * This essentially tests
+ * {@code DevicePolicyManagerService.findOwnerComponentIfNecessaryLocked()}. (which is private.)
+ *
+ * We didn't use to persist the DO component class name, but now we do, and the above method
+ * finds the right component from a package name upon migration.
+ */
+ public void testDeviceOwnerMigration() throws Exception {
+ checkDeviceOwnerWithMultipleDeviceAdmins();
+
+ // Overwrite the device owner setting and clears the clas name.
+ dpms.mOwners.setDeviceOwner(
+ new ComponentName(admin2.getPackageName(), ""),
+ "owner-name", DpmMockContext.CALLER_USER_HANDLE);
+ dpms.mOwners.writeDeviceOwner();
+
+ // Make sure the DO component name doesn't have a class name.
+ assertEquals("", dpms.getDeviceOwner().getClassName());
+
+ // Then create a new DPMS to have it load the settings from files.
+ initializeDpms();
+
+ // Now the DO component name is a full name.
+ // *BUT* because both admin1 and admin2 belong to the same package, we think admin1 is the
+ // DO.
+ assertEquals(admin1, dpms.getDeviceOwner());
+ }
+
public void testSetGetApplicationRestriction() {
setAsProfileOwner(admin1);
@@ -589,4 +783,95 @@ public class DevicePolicyManagerTest extends DpmTestBase {
dpm.setApplicationRestrictions(admin1, "pkg2", new Bundle());
assertEquals(0, dpm.getApplicationRestrictions(admin1, "pkg2").size());
}
+
+ public void testSetUserRestriction_asDo() throws Exception {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
+
+ // First, set DO.
+
+ // Call from a process on the system user.
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ // Make sure admin1 is installed on system user.
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+
+ // Call.
+ dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
+ assertTrue(dpm.setDeviceOwner(admin1, "owner-name",
+ UserHandle.USER_SYSTEM));
+
+ assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_SMS));
+ assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_SMS);
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
+
+ assertTrue(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_SMS));
+ assertTrue(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+ dpm.clearUserRestriction(admin1, UserManager.DISALLOW_SMS);
+
+ assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_SMS));
+ assertTrue(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+ dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
+
+ assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_SMS));
+ assertFalse(dpms.getDeviceOwnerAdminLocked().ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+ // TODO Check inner calls.
+ // TODO Make sure restrictions are written to the file.
+ }
+
+ public void testSetUserRestriction_asPo() {
+ setAsProfileOwner(admin1);
+
+ assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES));
+ assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
+ dpm.addUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
+
+ assertTrue(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES));
+ assertTrue(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+ dpm.clearUserRestriction(admin1, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
+
+ assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES));
+ assertTrue(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+ dpm.clearUserRestriction(admin1, UserManager.DISALLOW_OUTGOING_CALLS);
+
+ assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES));
+ assertFalse(dpms.getProfileOwnerAdminLocked(DpmMockContext.CALLER_USER_HANDLE)
+ .ensureUserRestrictions()
+ .getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
+
+ // TODO Check inner calls.
+ // TODO Make sure restrictions are written to the file.
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
index 325bf9ff0225..b80f3bf8d58d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTestable.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
import android.app.admin.DevicePolicyManager;
+import android.os.UserHandle;
/**
* Overrides {@link #DevicePolicyManager} for dependency injection.
@@ -31,6 +32,6 @@ public class DevicePolicyManagerTestable extends DevicePolicyManager {
@Override
public int myUserId() {
- return DpmMockContext.CALLER_USER_HANDLE;
+ return UserHandle.getUserId(dpms.context.binder.callingUid);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 3b30a37bd2c0..d1b483551809 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -22,11 +22,13 @@ import android.app.IActivityManager;
import android.app.NotificationManager;
import android.app.backup.IBackupManager;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.media.IAudioService;
import android.os.Bundle;
import android.os.Handler;
@@ -34,6 +36,7 @@ import android.os.PowerManager.WakeLock;
import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.UserManager;
+import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import android.view.IWindowManager;
@@ -43,8 +46,10 @@ import java.io.File;
import java.util.ArrayList;
import java.util.List;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
/**
* Context used throughout DPMS tests.
@@ -58,12 +63,12 @@ public class DpmMockContext extends MockContext {
/**
* UID corresponding to {@link #CALLER_USER_HANDLE}.
*/
- public static final int CALLER_UID = UserHandle.PER_USER_RANGE * CALLER_USER_HANDLE + 123;
+ public static final int CALLER_UID = UserHandle.getUid(CALLER_USER_HANDLE, 20123);
/**
* UID used when a caller is on the system user.
*/
- public static final int CALLER_SYSTEM_USER_UID = 123;
+ public static final int CALLER_SYSTEM_USER_UID = 20321;
/**
* PID of the caller.
@@ -155,6 +160,33 @@ public class DpmMockContext extends MockContext {
}
}
+ public static class SettingsForMock {
+ int settingsSecureGetIntForUser(String name, int def, int userHandle) {
+ return 0;
+ }
+
+ void settingsSecurePutIntForUser(String name, int value, int userHandle) {
+ }
+
+ void settingsSecurePutStringForUser(String name, String value, int userHandle) {
+ }
+
+ void settingsGlobalPutStringForUser(String name, String value, int userHandle) {
+ }
+
+ void settingsSecurePutInt(String name, int value) {
+ }
+
+ void settingsGlobalPutInt(String name, int value) {
+ }
+
+ void settingsSecurePutString(String name, String value) {
+ }
+
+ void settingsGlobalPutString(String name, String value) {
+ }
+ }
+
public final Context realTestContext;
/**
@@ -164,6 +196,9 @@ public class DpmMockContext extends MockContext {
*/
public final Context spiedContext;
+ public final File dataDir;
+ public final File systemUserDataDir;
+
public final MockBinder binder;
public final EnvironmentForMock environment;
public final SystemPropertiesForMock systemProperties;
@@ -178,14 +213,22 @@ public class DpmMockContext extends MockContext {
public final IBackupManager ibackupManager;
public final IAudioService iaudioService;
public final LockPatternUtils lockPatternUtils;
+ public final SettingsForMock settings;
+ public final MockContentResolver contentResolver;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
public final List<String> callerPermissions = new ArrayList<>();
- public DpmMockContext(Context context) {
+ private final ArrayList<UserInfo> mUserInfos = new ArrayList<>();
+
+ public DpmMockContext(Context context, File dataDir) {
realTestContext = context;
+
+ this.dataDir = dataDir;
+ DpmTestUtils.clearDir(dataDir);
+
binder = new MockBinder();
environment = mock(EnvironmentForMock.class);
systemProperties= mock(SystemPropertiesForMock.class);
@@ -200,11 +243,51 @@ public class DpmMockContext extends MockContext {
ibackupManager = mock(IBackupManager.class);
iaudioService = mock(IAudioService.class);
lockPatternUtils = mock(LockPatternUtils.class);
+ settings = mock(SettingsForMock.class);
// Package manager is huge, so we use a partial mock instead.
packageManager = spy(context.getPackageManager());
spiedContext = mock(Context.class);
+
+ contentResolver = new MockContentResolver();
+
+ // Add the system user
+ systemUserDataDir = addUser(UserHandle.USER_SYSTEM, UserInfo.FLAG_PRIMARY);
+
+ // System user is always running.
+ setUserRunning(UserHandle.USER_SYSTEM, true);
+ }
+
+ public File addUser(int userId, int flags) {
+
+ // Set up (default) UserInfo for CALLER_USER_HANDLE.
+ final UserInfo uh = new UserInfo(userId, "user" + userId, flags);
+ when(userManager.getUserInfo(eq(userId))).thenReturn(uh);
+
+ mUserInfos.add(uh);
+ when(userManager.getUsers()).thenReturn(mUserInfos);
+
+ // Create a data directory.
+ final File dir = new File(dataDir, "user" + userId);
+ DpmTestUtils.clearDir(dir);
+
+ when(environment.getUserSystemDirectory(eq(userId))).thenReturn(dir);
+ return dir;
+ }
+
+ /**
+ * Add multiple users at once. They'll all have flag 0.
+ */
+ public void addUsers(int... userIds) {
+ for (int userId : userIds) {
+ addUser(userId, 0);
+ }
+ }
+
+ public void setUserRunning(int userId, boolean isRunning) {
+ when(userManager.isUserRunning(MockUtils.checkUserHandle(userId)))
+ .thenReturn(isRunning);
}
@Override
@@ -399,4 +482,9 @@ public class DpmMockContext extends MockContext {
public void unregisterReceiver(BroadcastReceiver receiver) {
spiedContext.unregisterReceiver(receiver);
}
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return contentResolver;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 6f9f6ab7c5b9..63bf12558dd1 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -34,10 +34,9 @@ public abstract class DpmTestBase extends AndroidTestCase {
super.setUp();
mRealTestContext = super.getContext();
- mMockContext = new DpmMockContext(super.getContext());
- dataDir = new File(mRealTestContext.getCacheDir(), "test-data");
- DpmTestUtils.clearDir(dataDir);
+ mMockContext = new DpmMockContext(
+ mRealTestContext, new File(mRealTestContext.getCacheDir(), "test-data"));
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
index 44a851abe476..7506273accb7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestUtils.java
@@ -17,6 +17,8 @@
package com.android.server.devicepolicy;
import android.os.FileUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.Log;
import android.util.Printer;
@@ -41,6 +43,15 @@ public class DpmTestUtils {
return list == null ? 0 : list.size();
}
+ public static <T extends Parcelable> T cloneParcelable(T source) {
+ Parcel p = Parcel.obtain();
+ p.writeParcelable(source, 0);
+ p.setDataPosition(0);
+ final T clone = p.readParcelable(DpmTestUtils.class.getClassLoader());
+ p.recycle();
+ return clone;
+ }
+
public static Printer LOG_PRINTER = new Printer() {
@Override
public void println(String x) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index a07d61520fa4..79845d21281f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -21,6 +21,7 @@ import com.android.server.devicepolicy.DevicePolicyManagerServiceTestable.Owners
import android.content.ComponentName;
import android.content.pm.UserInfo;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Log;
import java.io.BufferedReader;
@@ -28,6 +29,8 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.ArrayList;
import static org.mockito.Mockito.when;
@@ -69,22 +72,12 @@ public class OwnersTest extends DpmTestBase {
}
}
- private void addUsersToUserManager(int... userIds) {
- final ArrayList<UserInfo> userInfos = new ArrayList<>();
- for (int userId : userIds) {
- final UserInfo ui = new UserInfo();
- ui.id = userId;
- userInfos.add(ui);
- }
- when(getContext().userManager.getUsers()).thenReturn(userInfos);
- }
-
public void testUpgrade01() throws Exception {
- addUsersToUserManager(10, 11, 20, 21);
+ getContext().addUsers(10, 11, 20, 21);
// First, migrate.
{
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
readAsset("OwnersTest/test01/input.xml"));
@@ -104,30 +97,28 @@ public class OwnersTest extends DpmTestBase {
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
}
// Then re-read and check.
{
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
owners.load();
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
}
}
public void testUpgrade02() throws Exception {
- addUsersToUserManager(10, 11, 20, 21);
+ getContext().addUsers(10, 11, 20, 21);
// First, migrate.
{
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
readAsset("OwnersTest/test02/input.xml"));
@@ -149,14 +140,13 @@ public class OwnersTest extends DpmTestBase {
assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
}
// Then re-read and check.
{
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
owners.load();
assertTrue(owners.hasDeviceOwner());
@@ -164,18 +154,17 @@ public class OwnersTest extends DpmTestBase {
assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
}
}
public void testUpgrade03() throws Exception {
- addUsersToUserManager(10, 11, 20, 21);
+ getContext().addUsers(10, 11, 20, 21);
// First, migrate.
{
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
readAsset("OwnersTest/test03/input.xml"));
@@ -194,7 +183,6 @@ public class OwnersTest extends DpmTestBase {
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(2, owners.getProfileOwnerKeys().size());
@@ -212,12 +200,11 @@ public class OwnersTest extends DpmTestBase {
// Then re-read and check.
{
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
owners.load();
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(2, owners.getProfileOwnerKeys().size());
@@ -235,11 +222,11 @@ public class OwnersTest extends DpmTestBase {
}
public void testUpgrade04() throws Exception {
- addUsersToUserManager(10, 11, 20, 21);
+ getContext().addUsers(10, 11, 20, 21);
// First, migrate.
{
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
readAsset("OwnersTest/test04/input.xml"));
@@ -261,8 +248,6 @@ public class OwnersTest extends DpmTestBase {
assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
- assertTrue(owners.hasDeviceInitializer());
- assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName());
assertNotNull(owners.getSystemUpdatePolicy());
assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType());
@@ -281,7 +266,7 @@ public class OwnersTest extends DpmTestBase {
// Then re-read and check.
{
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
owners.load();
assertTrue(owners.hasDeviceOwner());
@@ -289,8 +274,6 @@ public class OwnersTest extends DpmTestBase {
assertEquals("com.google.android.testdpc", owners.getDeviceOwnerPackageName());
assertEquals(UserHandle.USER_SYSTEM, owners.getDeviceOwnerUserId());
- assertTrue(owners.hasDeviceInitializer());
- assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName());
assertNotNull(owners.getSystemUpdatePolicy());
assertEquals(5, owners.getSystemUpdatePolicy().getPolicyType());
@@ -309,11 +292,11 @@ public class OwnersTest extends DpmTestBase {
}
public void testUpgrade05() throws Exception {
- addUsersToUserManager(10, 11, 20, 21);
+ getContext().addUsers(10, 11, 20, 21);
// First, migrate.
{
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
readAsset("OwnersTest/test05/input.xml"));
@@ -323,7 +306,8 @@ public class OwnersTest extends DpmTestBase {
// The legacy file should be removed.
assertFalse(owners.getLegacyConfigFileWithTestOverride().exists());
- assertTrue(owners.getDeviceOwnerFileWithTestOverride().exists());
+ // Note device initializer is no longer supported. No need to write the DO file.
+ assertFalse(owners.getDeviceOwnerFileWithTestOverride().exists());
assertFalse(owners.getProfileOwnerFileWithTestOverride(10).exists());
assertFalse(owners.getProfileOwnerFileWithTestOverride(11).exists());
@@ -332,8 +316,6 @@ public class OwnersTest extends DpmTestBase {
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertTrue(owners.hasDeviceInitializer());
- assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
@@ -341,14 +323,12 @@ public class OwnersTest extends DpmTestBase {
// Then re-read and check.
{
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
owners.load();
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertTrue(owners.hasDeviceInitializer());
- assertEquals("com.google.android.testdpcx", owners.getDeviceInitializerPackageName());
assertNull(owners.getSystemUpdatePolicy());
assertEquals(0, owners.getProfileOwnerKeys().size());
@@ -356,11 +336,11 @@ public class OwnersTest extends DpmTestBase {
}
public void testUpgrade06() throws Exception {
- addUsersToUserManager(10, 11, 20, 21);
+ getContext().addUsers(10, 11, 20, 21);
// First, migrate.
{
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
readAsset("OwnersTest/test06/input.xml"));
@@ -378,7 +358,6 @@ public class OwnersTest extends DpmTestBase {
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertEquals(0, owners.getProfileOwnerKeys().size());
assertNotNull(owners.getSystemUpdatePolicy());
@@ -387,12 +366,11 @@ public class OwnersTest extends DpmTestBase {
// Then re-read and check.
{
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
owners.load();
assertFalse(owners.hasDeviceOwner());
assertEquals(UserHandle.USER_NULL, owners.getDeviceOwnerUserId());
- assertFalse(owners.hasDeviceInitializer());
assertEquals(0, owners.getProfileOwnerKeys().size());
assertNotNull(owners.getSystemUpdatePolicy());
@@ -401,9 +379,9 @@ public class OwnersTest extends DpmTestBase {
}
public void testRemoveExistingFiles() throws Exception {
- addUsersToUserManager(10, 11, 20, 21);
+ getContext().addUsers(10, 11, 20, 21);
- final OwnersTestable owners = new OwnersTestable(getContext(), dataDir);
+ final OwnersTestable owners = new OwnersTestable(getContext());
// First, migrate to create new-style config files.
createLegacyFile(owners.getLegacyConfigFileWithTestOverride(),
@@ -418,7 +396,6 @@ public class OwnersTest extends DpmTestBase {
assertTrue(owners.getProfileOwnerFileWithTestOverride(11).exists());
// Then clear all information and save.
- owners.clearDeviceInitializer();
owners.clearDeviceOwner();
owners.clearSystemUpdatePolicy();
owners.removeProfileOwner(10);
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7f813ec59313..3160e3961495 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -753,7 +753,7 @@ public class UsbDeviceManager {
UsbManager.USB_FUNCTION_MTP)
|| UsbManager.containsFunction(mCurrentFunctions,
UsbManager.USB_FUNCTION_PTP);
- if (active && mCurrentUser != UserHandle.USER_NULL) {
+ if (mUsbDataUnlocked && active && mCurrentUser != UserHandle.USER_NULL) {
Slog.v(TAG, "Current user switched to " + mCurrentUser
+ "; resetting USB host stack for MTP or PTP");
setEnabledFunctions(mCurrentFunctions, true);
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index cdb0bf257baa..5c6416884aa5 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -28,6 +28,7 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -160,6 +161,7 @@ public final class PhoneAccount implements Parcelable {
private final CharSequence mShortDescription;
private final List<String> mSupportedUriSchemes;
private final Icon mIcon;
+ private final Bundle mExtras;
private boolean mIsEnabled;
/**
@@ -175,6 +177,7 @@ public final class PhoneAccount implements Parcelable {
private CharSequence mShortDescription;
private List<String> mSupportedUriSchemes = new ArrayList<String>();
private Icon mIcon;
+ private Bundle mExtras;
private boolean mIsEnabled = false;
/**
@@ -300,6 +303,20 @@ public final class PhoneAccount implements Parcelable {
}
/**
+ * Specifies the extras associated with the {@link PhoneAccount}.
+ * <p>
+ * {@code PhoneAccount}s only support extra values of type: {@link String}, {@link Integer},
+ * and {@link Boolean}. Extras which are not of these types are ignored.
+ *
+ * @param extras
+ * @return
+ */
+ public Builder setExtras(Bundle extras) {
+ mExtras = extras;
+ return this;
+ }
+
+ /**
* Sets the enabled state of the phone account.
*
* @param isEnabled The enabled state.
@@ -332,6 +349,7 @@ public final class PhoneAccount implements Parcelable {
mLabel,
mShortDescription,
mSupportedUriSchemes,
+ mExtras,
mIsEnabled);
}
}
@@ -346,6 +364,7 @@ public final class PhoneAccount implements Parcelable {
CharSequence label,
CharSequence shortDescription,
List<String> supportedUriSchemes,
+ Bundle extras,
boolean isEnabled) {
mAccountHandle = account;
mAddress = address;
@@ -356,6 +375,7 @@ public final class PhoneAccount implements Parcelable {
mLabel = label;
mShortDescription = shortDescription;
mSupportedUriSchemes = Collections.unmodifiableList(supportedUriSchemes);
+ mExtras = extras;
mIsEnabled = isEnabled;
}
@@ -455,6 +475,18 @@ public final class PhoneAccount implements Parcelable {
}
/**
+ * The extras associated with this {@code PhoneAccount}.
+ * <p>
+ * A {@link ConnectionService} may provide implementation specific information about the
+ * {@link PhoneAccount} via the extras.
+ *
+ * @return The extras.
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
* The icon to represent this {@code PhoneAccount}.
*
* @return The icon.
@@ -552,6 +584,8 @@ public final class PhoneAccount implements Parcelable {
out.writeInt(1);
mIcon.writeToParcel(out, flags);
}
+
+ out.writeBundle(mExtras);
out.writeByte((byte) (mIsEnabled ? 1 : 0));
}
@@ -594,6 +628,7 @@ public final class PhoneAccount implements Parcelable {
} else {
mIcon = null;
}
+ mExtras = in.readBundle();
mIsEnabled = in.readByte() == 1;
}
@@ -610,6 +645,8 @@ public final class PhoneAccount implements Parcelable {
sb.append(scheme)
.append(" ");
}
+ sb.append(" Extras : ");
+ sb.append(mExtras);
sb.append("]");
return sb.toString();
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e57964a96182..ba91254b01a8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -304,6 +304,31 @@ public class CarrierConfigManager {
"carrier_instant_lettering_escaped_chars_string";
/**
+ * When IMS instant lettering is available for a carrier (see
+ * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), determines the character encoding
+ * which will be used when determining the length of messages. Used in the InCall UI to limit
+ * the number of characters the user may type. If empty-string, the instant lettering
+ * message size limit will be enforced on a 1:1 basis. That is, each character will count
+ * towards the messages size limit as a single bye. If a character encoding is specified, the
+ * message size limit will be based on the number of bytes in the message per the specified
+ * encoding.
+ * @hide
+ */
+ public static final String KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING =
+ "carrier_instant_lettering_encoding_string";
+
+ /**
+ * When IMS instant lettering is available for a carrier (see
+ * {@link #KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL}), the length limit for messages. Used
+ * in the InCall UI to ensure the user cannot enter more characters than allowed by the carrier.
+ * See also {@link #KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING} for more information on how
+ * the length of the message is calculated.
+ * @hide
+ */
+ public static final String KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT =
+ "carrier_instant_lettering_length_limit_int";
+
+ /**
* If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
* this is the value that should be used instead. A configuration value of
* RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
@@ -508,6 +533,8 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL, true);
sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, "");
sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, "");
+ sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING, "");
+ sDefaults.putInt(KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT, 64);
sDefaults.putBoolean(KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL, false);
sDefaults.putBoolean(KEY_DTMF_TYPE_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
index b458d9b14096..7628c5c24941 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MultiProducerActivity.java
@@ -188,7 +188,7 @@ public class MultiProducerActivity extends Activity implements OnClickListener {
// This call should be done while the rendernode's displaylist is produced.
// For simplicity of this test we do this before we kick off the draw.
mContent.getLocationInSurface(surfaceOrigin);
- mRenderer.setContentOverdrawProtectionBounds(surfaceOrigin[0], surfaceOrigin[1],
+ mRenderer.setContentDrawBounds(surfaceOrigin[0], surfaceOrigin[1],
surfaceOrigin[0] + mContent.getWidth(),
surfaceOrigin[1] + mContent.getHeight());
// Determine new position for frame.
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
index e3e1d34f193e..b4e0c707d3f3 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionMeasuringActivity.java
@@ -106,12 +106,12 @@ public class SurfaceCompositionMeasuringActivity extends Activity implements OnC
class CompositorScore {
double mSurfaces;
- double mBitrate;
+ double mBandwidth;
@Override
public String toString() {
return DOUBLE_FORMAT.format(mSurfaces) + " surfaces. " +
- "Bitrate: " + getReadableMemory((long)mBitrate) + "/s";
+ "Bandwidth: " + getReadableMemory((long)mBandwidth) + "/s";
}
}
@@ -131,7 +131,7 @@ public class SurfaceCompositionMeasuringActivity extends Activity implements OnC
score.mSurfaces = measureCompositionScore(new Measurement(0, 60.0),
new Measurement(mViews.size() + 1, 0.0f), pixelFormat);
// Assume 32 bits per pixel.
- score.mBitrate = score.mSurfaces * mTargetFPS * mWidth * mHeight * 4.0;
+ score.mBandwidth = score.mSurfaces * mTargetFPS * mWidth * mHeight * 4.0;
//memAccessTask.stop();
return score;
}
diff --git a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
index 6e9e7390c2c1..3f04888da637 100644
--- a/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
+++ b/tests/SurfaceComposition/src/android/surfacecomposition/SurfaceCompositionTest.java
@@ -15,7 +15,9 @@
*/
package android.surfacecomposition;
+import android.app.Activity;
import android.graphics.PixelFormat;
+import android.os.Bundle;
import android.surfacecomposition.SurfaceCompositionMeasuringActivity.AllocationScore;
import android.surfacecomposition.SurfaceCompositionMeasuringActivity.CompositorScore;
import android.test.ActivityInstrumentationTestCase2;
@@ -25,6 +27,16 @@ import android.util.Log;
public class SurfaceCompositionTest extends
ActivityInstrumentationTestCase2<SurfaceCompositionMeasuringActivity> {
private final static String TAG = "SurfaceCompositionTest";
+ private final static String KEY_SURFACE_COMPOSITION_PERFORMANCE =
+ "surface-compoistion-peformance-sps";
+ private final static String KEY_SURFACE_COMPOSITION_BANDWITH =
+ "surface-compoistion-bandwidth-gbps";
+ private final static String KEY_SURFACE_ALLOCATION_PERFORMANCE_MEDIAN =
+ "surface-allocation-performance-median-sps";
+ private final static String KEY_SURFACE_ALLOCATION_PERFORMANCE_MIN =
+ "surface-allocation-performance-min-sps";
+ private final static String KEY_SURFACE_ALLOCATION_PERFORMANCE_MAX =
+ "surface-allocation-performance-max-sps";
// Pass threshold for major pixel formats.
private final static int[] TEST_PIXEL_FORMATS = new int[] {
@@ -53,6 +65,7 @@ public class SurfaceCompositionTest extends
@SmallTest
public void testSurfaceCompositionPerformance() {
+ Bundle status = new Bundle();
for (int i = 0; i < TEST_PIXEL_FORMATS.length; ++i) {
int pixelFormat = TEST_PIXEL_FORMATS[i];
String formatName = SurfaceCompositionMeasuringActivity.getPixelFormatInfo(pixelFormat);
@@ -62,11 +75,20 @@ public class SurfaceCompositionTest extends
"performance score. " + score.mSurfaces + " < " +
MIN_ACCEPTED_COMPOSITION_SCORE[i] + ".",
score.mSurfaces >= MIN_ACCEPTED_COMPOSITION_SCORE[i]);
+ // Send status only for TRANSLUCENT format.
+ if (pixelFormat == PixelFormat.TRANSLUCENT) {
+ status.putDouble(KEY_SURFACE_COMPOSITION_PERFORMANCE, score.mSurfaces);
+ // Put bandwidth in GBPS.
+ status.putDouble(KEY_SURFACE_COMPOSITION_BANDWITH, score.mBandwidth /
+ (1024.0 * 1024.0 * 1024.0));
+ }
}
+ getInstrumentation().sendStatus(Activity.RESULT_OK, status);
}
@SmallTest
public void testSurfaceAllocationPerformance() {
+ Bundle status = new Bundle();
for (int i = 0; i < TEST_PIXEL_FORMATS.length; ++i) {
int pixelFormat = TEST_PIXEL_FORMATS[i];
String formatName = SurfaceCompositionMeasuringActivity.getPixelFormatInfo(pixelFormat);
@@ -76,6 +98,13 @@ public class SurfaceCompositionTest extends
"performance score. " + score.mMedian + " < " +
MIN_ACCEPTED_ALLOCATION_SCORE[i] + ".",
score.mMedian >= MIN_ACCEPTED_ALLOCATION_SCORE[i]);
+ // Send status only for TRANSLUCENT format.
+ if (pixelFormat == PixelFormat.TRANSLUCENT) {
+ status.putDouble(KEY_SURFACE_ALLOCATION_PERFORMANCE_MEDIAN, score.mMedian);
+ status.putDouble(KEY_SURFACE_ALLOCATION_PERFORMANCE_MIN, score.mMin);
+ status.putDouble(KEY_SURFACE_ALLOCATION_PERFORMANCE_MAX, score.mMax);
+ }
}
+ getInstrumentation().sendStatus(Activity.RESULT_OK, status);
}
}
diff --git a/tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java b/tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java
deleted file mode 100644
index 4475fa483015..000000000000
--- a/tools/layoutlib/bridge/src/android/animation/AnimatorInflater_Delegate.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.animation;
-
-import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.content.res.Resources.Theme;
-import android.util.AttributeSet;
-
-/**
- * Delegate providing alternate implementation to static methods in {@link AnimatorInflater}.
- */
-public class AnimatorInflater_Delegate {
-
- @LayoutlibDelegate
- /*package*/ static Animator loadAnimator(Context context, int id)
- throws NotFoundException {
- return loadAnimator(context.getResources(), context.getTheme(), id);
- }
-
- @LayoutlibDelegate
- /*package*/ static Animator loadAnimator(Resources resources, Theme theme, int id)
- throws NotFoundException {
- return loadAnimator(resources, theme, id, 1);
- }
-
- @LayoutlibDelegate
- /*package*/ static Animator loadAnimator(Resources resources, Theme theme, int id,
- float pathErrorScale) throws NotFoundException {
- // This is a temporary fix to http://b.android.com/77865. This skips loading the
- // animation altogether.
- // TODO: Remove this override when Path.approximate() is supported.
- return new FakeAnimator();
- }
-
- @LayoutlibDelegate
- /*package*/ static ValueAnimator loadAnimator(Resources res, Theme theme,
- AttributeSet attrs, ValueAnimator anim, float pathErrorScale)
- throws NotFoundException {
- return AnimatorInflater.loadAnimator_Original(res, theme, attrs, anim, pathErrorScale);
- }
-}
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
index f4b1f2cd363f..0e392436d748 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
@@ -18,6 +18,7 @@ package android.content.res;
import com.android.SdkConstants;
import com.android.ide.common.rendering.api.ArrayResourceValue;
+import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.ResourceValue;
@@ -661,13 +662,18 @@ public final class BridgeResources extends Resources {
Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
if (value != null) {
- String v = value.getSecond().getValue();
+ ResourceValue resVal = value.getSecond();
+ String v = resVal.getValue();
if (v != null) {
if (ResourceHelper.parseFloatAttribute(value.getFirst(), v, outValue,
false /*requireUnit*/)) {
return;
}
+ if (resVal instanceof DensityBasedResourceValue) {
+ outValue.density =
+ ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue();
+ }
// else it's a string
outValue.type = TypedValue.TYPE_STRING;
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
index d858953b956c..60514b658649 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -59,6 +59,7 @@ import java.util.Set;
if (opts.inPremultiplied) {
bitmapCreateFlags.add(BitmapCreateFlags.PREMULTIPLIED);
}
+ opts.inScaled = false;
}
try {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index f8b3739b6a89..64cd5031346e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -35,6 +35,8 @@ import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
@@ -707,6 +709,12 @@ public final class Canvas_Delegate {
@Override
public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
Shape shape = pathDelegate.getJavaShape();
+ Rectangle2D bounds = shape.getBounds2D();
+ if (bounds.isEmpty()) {
+ // Apple JRE 1.6 doesn't like drawing empty shapes.
+ // http://b.android.com/178278
+ return;
+ }
int style = paintDelegate.getStyle();
if (style == Paint.Style.FILL.nativeInt ||
diff --git a/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java
new file mode 100644
index 000000000000..dd2978f5c414
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/PathMeasure_Delegate.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+
+/**
+ * Delegate implementing the native methods of {@link android.graphics.PathMeasure}
+ * <p/>
+ * Through the layoutlib_create tool, the original native methods of PathMeasure have been
+ * replaced by
+ * calls to methods of the same name in this delegate class.
+ * <p/>
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between it
+ * and the original PathMeasure class.
+ *
+ * @see DelegateManager
+ */
+public final class PathMeasure_Delegate {
+ // ---- delegate manager ----
+ private static final DelegateManager<PathMeasure_Delegate> sManager =
+ new DelegateManager<PathMeasure_Delegate>(PathMeasure_Delegate.class);
+
+ // ---- delegate data ----
+ // This governs how accurate the approximation of the Path is.
+ private static final float PRECISION = 0.002f;
+
+ /**
+ * Array containing the path points components. There are three components for each point:
+ * <ul>
+ * <li>Fraction along the length of the path that the point resides</li>
+ * <li>The x coordinate of the point</li>
+ * <li>The y coordinate of the point</li>
+ * </ul>
+ */
+ private float mPathPoints[];
+ private long mNativePath;
+
+ private PathMeasure_Delegate(long native_path, boolean forceClosed) {
+ mNativePath = native_path;
+ if (forceClosed && mNativePath != 0) {
+ // Copy the path and call close
+ mNativePath = Path_Delegate.init2(native_path);
+ Path_Delegate.native_close(mNativePath);
+ }
+
+ mPathPoints =
+ mNativePath != 0 ? Path_Delegate.native_approximate(mNativePath, PRECISION) : null;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long native_create(long native_path, boolean forceClosed) {
+ return sManager.addNewDelegate(new PathMeasure_Delegate(native_path, forceClosed));
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void native_destroy(long native_instance) {
+ sManager.removeJavaReferenceFor(native_instance);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean native_getPosTan(long native_instance, float distance, float pos[],
+ float tan[]) {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "PathMeasure.getPostTan is not supported.", null, null);
+ return false;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean native_getMatrix(long native_instance, float distance, long
+ native_matrix, int flags) {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "PathMeasure.getMatrix is not supported.", null, null);
+ return false;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean native_nextContour(long native_instance) {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "PathMeasure.nextContour is not supported.", null, null);
+ return false;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void native_setPath(long native_instance, long native_path, boolean
+ forceClosed) {
+ PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
+ assert pathMeasure != null;
+
+ if (forceClosed && native_path != 0) {
+ // Copy the path and call close
+ native_path = Path_Delegate.init2(native_path);
+ Path_Delegate.native_close(native_path);
+ }
+ pathMeasure.mNativePath = native_path;
+ pathMeasure.mPathPoints = Path_Delegate.native_approximate(native_path, PRECISION);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static float native_getLength(long native_instance) {
+ PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
+ assert pathMeasure != null;
+
+ if (pathMeasure.mPathPoints == null) {
+ return 0;
+ }
+
+ float length = 0;
+ int nPoints = pathMeasure.mPathPoints.length / 3;
+ for (int i = 1; i < nPoints; i++) {
+ length += Point2D.distance(
+ pathMeasure.mPathPoints[(i - 1) * 3 + 1],
+ pathMeasure.mPathPoints[(i - 1) * 3 + 2],
+ pathMeasure.mPathPoints[i*3 + 1],
+ pathMeasure.mPathPoints[i*3 + 2]);
+ }
+
+ return length;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean native_isClosed(long native_instance) {
+ PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
+ assert pathMeasure != null;
+
+ Path_Delegate path = Path_Delegate.getDelegate(pathMeasure.mNativePath);
+ if (path == null) {
+ return false;
+ }
+
+ PathIterator pathIterator = path.getJavaShape().getPathIterator(null);
+
+ int type = 0;
+ float segment[] = new float[6];
+ while (!pathIterator.isDone()) {
+ type = pathIterator.currentSegment(segment);
+ pathIterator.next();
+ }
+
+ // A path is a closed path if the last element is SEG_CLOSE
+ return type == PathIterator.SEG_CLOSE;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean native_getSegment(long native_instance, float startD, float stopD,
+ long native_dst_path, boolean startWithMoveTo) {
+ if (startD < 0) {
+ startD = 0;
+ }
+
+ if (startD >= stopD) {
+ return false;
+ }
+
+ PathMeasure_Delegate pathMeasure = sManager.getDelegate(native_instance);
+ assert pathMeasure != null;
+
+ if (pathMeasure.mPathPoints == null) {
+ return false;
+ }
+
+ float accLength = 0;
+ boolean isZeroLength = true; // Whether the output has zero length or not
+ int nPoints = pathMeasure.mPathPoints.length / 3;
+ for (int i = 0; i < nPoints; i++) {
+ float x = pathMeasure.mPathPoints[i * 3 + 1];
+ float y = pathMeasure.mPathPoints[i * 3 + 2];
+ if (accLength >= startD && accLength <= stopD) {
+ if (startWithMoveTo) {
+ startWithMoveTo = false;
+ Path_Delegate.native_moveTo(native_dst_path, x, y);
+ } else {
+ isZeroLength = false;
+ Path_Delegate.native_lineTo(native_dst_path, x, y);
+ }
+ }
+
+ if (i > 0) {
+ accLength += Point2D.distance(
+ pathMeasure.mPathPoints[(i - 1) * 3 + 1],
+ pathMeasure.mPathPoints[(i - 1) * 3 + 2],
+ pathMeasure.mPathPoints[i * 3 + 1],
+ pathMeasure.mPathPoints[i * 3 + 2]);
+ }
+ }
+
+ return !isZeroLength;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index 3c9a062719e2..a2a53fef7f1f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -36,6 +36,7 @@ import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
+import java.util.ArrayList;
/**
* Delegate implementing the native methods of android.graphics.Path
@@ -173,11 +174,8 @@ public final class Path_Delegate {
@LayoutlibDelegate
/*package*/ static boolean native_isEmpty(long nPath) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return true;
- }
+ return pathDelegate == null || pathDelegate.isEmpty();
- return pathDelegate.isEmpty();
}
@LayoutlibDelegate
@@ -488,54 +486,44 @@ public final class Path_Delegate {
@LayoutlibDelegate
/*package*/ static float[] native_approximate(long nPath, float error) {
- Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED, "Path.approximate() not fully supported",
- null);
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return null;
}
- PathIterator pathIterator = pathDelegate.mPath.getPathIterator(null);
- float[] tmp = new float[6];
- float[] coords = new float[6];
- boolean isFirstPoint = true;
- while (!pathIterator.isDone()) {
- int type = pathIterator.currentSegment(tmp);
- switch (type) {
- case PathIterator.SEG_MOVETO:
- case PathIterator.SEG_LINETO:
- store(tmp, coords, 1, isFirstPoint);
- break;
- case PathIterator.SEG_QUADTO:
- store(tmp, coords, 2, isFirstPoint);
- break;
- case PathIterator.SEG_CUBICTO:
- store(tmp, coords, 3, isFirstPoint);
- break;
- case PathIterator.SEG_CLOSE:
- // No points returned.
+ // Get a FlatteningIterator
+ PathIterator iterator = pathDelegate.getJavaShape().getPathIterator(null, error);
+
+ float segment[] = new float[6];
+ float totalLength = 0;
+ ArrayList<Point2D.Float> points = new ArrayList<Point2D.Float>();
+ Point2D.Float previousPoint = null;
+ while (!iterator.isDone()) {
+ int type = iterator.currentSegment(segment);
+ Point2D.Float currentPoint = new Point2D.Float(segment[0], segment[1]);
+ // MoveTo shouldn't affect the length
+ if (previousPoint != null && type != PathIterator.SEG_MOVETO) {
+ totalLength += currentPoint.distance(previousPoint);
}
- isFirstPoint = false;
- pathIterator.next();
+ previousPoint = currentPoint;
+ points.add(currentPoint);
+ iterator.next();
}
- if (isFirstPoint) {
- // No points found
- return new float[0];
- } else {
- return coords;
- }
- }
- private static void store(float[] src, float[] dst, int count, boolean isFirst) {
- if (isFirst) {
- dst[0] = 0; // fraction
- dst[1] = src[0]; // abscissa
- dst[2] = src[1]; // ordinate
- }
- if (count > 1 || !isFirst) {
- dst[3] = 1;
- dst[4] = src[2 * count - 2];
- dst[5] = src[2 * count - 1];
+ int nPoints = points.size();
+ float[] result = new float[nPoints * 3];
+ previousPoint = null;
+ for (int i = 0; i < nPoints; i++) {
+ Point2D.Float point = points.get(i);
+ float distance = previousPoint != null ? (float) previousPoint.distance(point) : .0f;
+ result[i * 3] = distance / totalLength;
+ result[i * 3 + 1] = point.x;
+ result[i * 3 + 2] = point.y;
+
+ totalLength += distance;
+ previousPoint = point;
}
+
+ return result;
}
// ---- Private helper methods ----
@@ -735,6 +723,9 @@ public final class Path_Delegate {
*/
private void cubicTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
+ if (isEmpty()) {
+ mPath.moveTo(0, 0);
+ }
mPath.curveTo(x1, y1, x2, y2, mLastX = x3, mLastY = y3);
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java
new file mode 100644
index 000000000000..a3ad2aac7d3d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/GradientDrawable_Delegate.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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 com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.Path;
+import android.graphics.drawable.GradientDrawable.GradientState;
+
+import java.lang.reflect.Field;
+
+/**
+ * Delegate implementing the native methods of {@link GradientDrawable}
+ *
+ * Through the layoutlib_create tool, the original native methods of GradientDrawable have been
+ * replaced by calls to methods of the same name in this delegate class.
+ */
+public class GradientDrawable_Delegate {
+
+ /**
+ * The ring can be built either by drawing full circles, or by drawing arcs in case the
+ * circle isn't complete. LayoutLib cannot handle drawing full circles (requires path
+ * subtraction). So, if we need to draw full circles, we switch to drawing 99% circle.
+ */
+ @LayoutlibDelegate
+ /*package*/ static Path buildRing(GradientDrawable thisDrawable, GradientState st) {
+ boolean useLevel = st.mUseLevelForShape;
+ int level = thisDrawable.getLevel();
+ // 10000 is the max level. See android.graphics.drawable.Drawable#getLevel()
+ float sweep = useLevel ? (360.0f * level / 10000.0f) : 360f;
+ Field mLevel = null;
+ if (sweep >= 360 || sweep <= -360) {
+ st.mUseLevelForShape = true;
+ // Use reflection to set the value of the field to prevent setting the drawable to
+ // dirty again.
+ try {
+ mLevel = Drawable.class.getDeclaredField("mLevel");
+ mLevel.setAccessible(true);
+ mLevel.setInt(thisDrawable, 9999); // set to one less than max.
+ } catch (NoSuchFieldException e) {
+ // The field has been removed in a recent framework change. Fall back to old
+ // buggy behaviour.
+ } catch (IllegalAccessException e) {
+ // We've already set the field to be accessible.
+ assert false;
+ }
+ }
+ Path path = thisDrawable.buildRing_Original(st);
+ st.mUseLevelForShape = useLevel;
+ if (mLevel != null) {
+ try {
+ mLevel.setInt(thisDrawable, level);
+ } catch (IllegalAccessException e) {
+ assert false;
+ }
+ }
+ return path;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index 771c3c85d2f5..c2d8d0c3c77b 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -91,14 +91,6 @@ public final class BridgeWindow implements IWindow {
}
@Override
- public void onAnimationStarted(int remainingFrameCount) {
- }
-
- @Override
- public void onAnimationStopped() {
- }
-
- @Override
public void dispatchWindowShown() {
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 499bea42007f..e480eadc0cf6 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -152,7 +152,6 @@ public final class CreateInfo implements ICreateInfo {
* The list of methods to rewrite as delegates.
*/
public final static String[] DELEGATE_METHODS = new String[] {
- "android.animation.AnimatorInflater#loadAnimator", // TODO: remove when Path.approximate() is supported.
"android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
"android.content.res.Resources$Theme#obtainStyledAttributes",
"android.content.res.Resources$Theme#resolveAttribute",
@@ -162,6 +161,7 @@ public final class CreateInfo implements ICreateInfo {
"android.content.res.TypedArray#getValueAt",
"android.content.res.TypedArray#obtain",
"android.graphics.BitmapFactory#finishDecode",
+ "android.graphics.drawable.GradientDrawable#buildRing",
"android.graphics.Typeface#getSystemFontConfigLocation",
"android.os.Handler#sendMessageAtTime",
"android.os.HandlerThread#run",
@@ -228,6 +228,7 @@ public final class CreateInfo implements ICreateInfo {
"android.graphics.Path",
"android.graphics.PathDashPathEffect",
"android.graphics.PathEffect",
+ "android.graphics.PathMeasure",
"android.graphics.PixelXorXfermode",
"android.graphics.PorterDuffColorFilter",
"android.graphics.PorterDuffXfermode",